updated US CDC website link to current immunization VIS page (#7855)
[openemr.git] / src / Services / QuestionnaireTraits.php
blob69b4023faa3478932f44c7d9dd471c6cddec4524
1 <?php
3 namespace OpenEMR\Services;
5 use Exception;
6 use OpenEMR\FHIR\R4\FHIRDomainResource\FHIRQuestionnaireResponse;
7 use OpenEMR\FHIR\R4\FHIRElement\FHIRCanonical;
8 use OpenEMR\FHIR\R4\FHIRElement\FHIRId;
9 use OpenEMR\FHIR\R4\FHIRElement\FHIRReference;
10 use OpenEMR\FHIR\R4\FHIRElement\FHIRString;
11 use OpenEMR\FHIR\R4\PHPFHIRResponseParser;
13 trait QuestionnaireTraits
15 /**
16 * @param $group
17 * @param $item
18 * @param $displayName
19 * @return mixed|string|null
21 public function getInstrumentName($group, $item = null, $displayName = true)
23 if ($item && $this->isRepeating($item)) {
24 $group = $item;
27 $name = $this->getText($group);
28 $linkId = null;
29 if (empty($name)) {
30 $name = "Top Level";
31 } else {
32 $linkId = $this->getLinkId($group) ?? '';
34 if (!$displayName) {
35 return $linkId;
38 return $name;
41 /**
42 * @param $item
43 * @return bool
45 public function isRepeating($item): bool
47 if ($item->get_fhirElementName() !== 'Questionnaire.Item') {
48 return false;
50 $repeats = $item->getRepeats();
52 return $repeats && $repeats->getValue();
55 /**
56 * @param $o
57 * @return mixed|null
59 public function getValue($o)
61 if ($o === null) {
62 return null;
64 while (method_exists((object)$o, 'getValue')) {
65 $o = $o->getValue();
68 return $o;
71 /**
72 * @param $item
73 * @return mixed|void
75 public function getText($item)
77 $text = $item->getText();
78 if ($text && method_exists($text, "getValue")) {
79 return $text->getValue();
83 /**
84 * @param $item
85 * @return mixed|null
87 public function getLinkId($item)
89 if (!in_array($item->get_fhirElementName(), ['Questionnaire.Item', 'QuestionnaireResponse.Item'])) {
90 return null;
93 return $item->getLinkId()->getValue();
96 /**
97 * @param $group
98 * @param $item
99 * @return mixed|null
101 public function getInstrumentPath($group, $item = null)
103 if ($item && $this->isRepeating($item)) {
104 $group = $item;
107 return $this->getLinkId($group);
111 * @param $item
112 * @return mixed|void|null
114 public function getType($item)
116 if ($item->get_fhirElementName() === 'Questionnaire.Item') {
117 $type = $this->getValue($item->getType());
118 return $type;
123 * @param $data
124 * @return array|object|string
125 * @throws Exception
127 public function parse($data, $autoLoad = false)
129 $parser = new PHPFHIRResponseParser($autoLoad);
130 if (is_array($data) || is_object($data)) {
131 // this is so the parser can set up necessary namespaces
132 $data = $this->jsonSerialize($data);
134 return $parser->parse($data);
138 * @param $fhirObjectOrArray
139 * @return false|string
140 * @throws Exception
142 public function jsonSerialize($fhirObjectOrArray): mixed
144 $a = $this->fhirObjectToArray($fhirObjectOrArray);
145 return json_encode($a, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
149 * @param $fhirObjectOrArray
150 * @return array|mixed
151 * @throws Exception
153 public function fhirObjectToArray($fhirObjectOrArray)
155 if (is_array($fhirObjectOrArray)) {
156 return $fhirObjectOrArray;
157 } elseif (is_object($fhirObjectOrArray)) {
158 $a = $fhirObjectOrArray->jsonSerialize();
159 $a = json_decode(json_encode($a), true);
160 $handle = function (&$a) use (&$handle) {
161 foreach ($a as $key => &$value) {
162 if (gettype($key) === 'string' && $key[0] === '_') {
163 unset($a[$key]);
164 continue;
166 if (is_array($value)) {
167 $handle($value);
171 $handle($a);
172 return $a;
173 } else {
174 throw new Exception(xlt('A valid FHIR object or array must be specified.'));
179 * @param $FHIRObject
180 * @return false|string
182 public function xmlSerialize($FHIRObject)
184 $dom = dom_import_simplexml($FHIRObject->xmlSerialize())->ownerDocument;
185 $dom->formatOutput = true;
187 return $dom->saveXML();
191 * @param $mixed
192 * @return array|mixed|string
193 * @throws Exception
195 public function getTypedValue($mixed)
197 $arr = '';
198 $vs = $this->fhirObjectToArray($mixed);
199 foreach ($vs as $k => $v) {
200 $a[$k] = $v;
201 $arr = $this->parseAnswer($a, true);
204 return $arr;
208 * @param $answer
209 * @param $display
210 * @return array|mixed
212 private function parseAnswer($answer, $display = false)
214 $obv = array();
215 $type = key($answer);
216 switch ($type) {
217 case "valueBoolean":
218 $obv['type'] = 'boolean';
219 $obv['display'] = $answer[$type];
220 break;
221 case "valueDecimal":
222 $obv['type'] = 'decimal';
223 $obv['display'] = $answer[$type];
224 break;
225 case "valueInteger":
226 $obv['type'] = 'integer';
227 $obv['display'] = $answer[$type];
228 break;
229 case "valueDate":
230 $obv['type'] = 'date';
231 $obv['display'] = $answer[$type];
232 break;
233 case "valueDateTime":
234 $obv['type'] = 'datetime';
235 $obv['display'] = $answer[$type];
236 break;
237 case "valueTime":
238 $obv['type'] = 'time';
239 $obv['display'] = $answer[$type];
240 break;
241 case "valueString":
242 $obv['type'] = 'string';
243 $obv['display'] = $answer[$type];
244 break;
245 case "valueUri":
246 $obv['type'] = 'uri';
247 $obv['display'] = $answer[$type];
248 break;
249 case "valueCoding":
250 $obv = array(
251 'type' => 'coding',
252 'system' => $answer[$type]['system'] ?? null,
253 'code' => $answer[$type]['code'],
254 'display' => $answer[$type]['display'],
256 break;
257 case "valueQuantity":
258 $obv['type'] = 'quantity';
259 $obv['display'] = $answer[$type];
260 break;
261 case "valueAttachment":
262 // todo
263 break;
264 case "valueReference":
265 break;
267 if ($display) {
268 return $obv['display'];
270 return $obv;
274 * @param $item
275 * @return mixed|string
277 public function getFieldName($item)
279 $n = $item->getLinkId()->getValue();
280 return $n ?? null;
284 * @param $timestamp
285 * @return mixed
287 public function formatFHIRDateTime($timestamp)
289 return $this->getDateTime($timestamp)->format('Y-m-d\TH:i:sP');
293 * @param $mixed
294 * @return mixed|DateTime
296 private function getDateTime($mixed)
298 $type = gettype($mixed);
300 if ($type === 'string') {
301 return new \DateTime($mixed);
302 } elseif ($type === 'integer') {
303 $d = new \DateTime();
304 $d->setTimestamp($mixed);
305 return $d;
306 } else {
307 // Assume this is already a DateTime object.
308 return $mixed;
313 * @param $timestamp
314 * @return mixed
316 public function formatFHIRTime($timestamp)
318 return $this->getDateTime($timestamp)->format('H:i:s');
322 * @param $mixed
323 * @return mixed
325 public function formatOeDateTime($mixed)
327 return $this->getDateTime($mixed)->format('Y-m-d H:i');
331 * @param $mixed
332 * @return mixed
334 public function formatOeDateTimeWithSeconds($mixed)
336 return $this->getDateTime($mixed)->format('Y-m-d H:i:s');
340 * @param $mixed
341 * @return mixed
343 public function formatOeTime($mixed)
345 return $this->getDateTime($mixed)->format('H:i');
349 * @param $response
350 * @param bool $encode
351 * @return array|false|string
352 * @throws Exception
354 public function extractResponseMetaData($response, bool $encode = false)
356 if (is_string($response)) {
357 $response = json_decode($response, true);
359 $fhirObj = new FHIRQuestionnaireResponse($response);
360 $meta['id'] = $fhirObj->getId();
361 $meta['encounter'] = $fhirObj->getEncounter();
362 $meta['qid'] = $this->getValue($fhirObj->getQuestionnaire());
363 if ($encode) {
364 return $this->jsonSerialize($meta);
366 return $meta;
370 * @param $response
371 * @param $meta
372 * @return mixed
373 * @throws Exception
375 public function insertResponseMetaData($response, $meta, bool $encode = true)
377 if (is_string($response)) {
378 $response = json_decode($response, true);
380 if (is_string($meta)) {
381 $meta = json_decode($meta, true);
383 $fhirObj = new FHIRQuestionnaireResponse($response);
384 $fhirObj->setId(new FHIRId($meta['id']));
385 $encRef = new FHIRReference();
386 $encRef->setReference(new FHIRString($meta['encounter']));
387 $fhirObj->setEncounter($encRef);
388 $fhirObj->setQuestionnaire(new FHIRCanonical($meta['qid']));
389 if ($encode) {
390 return $this->jsonSerialize($fhirObj);
392 return $fhirObj;
396 * @param $answer
397 * @param bool $display
398 * @return array|mixed
400 private function setAnswer($answer, bool $display = false)
402 $ans_set = [];
403 if (count($answer ?? []) > 1) {
404 foreach ($answer as $ans) {
405 $a = $this->parseAnswer($ans, $display);
406 $ans_set[] = $a;
408 } elseif (!empty($answer[0])) {
409 $a = $this->parseAnswer($answer[0], $display);
410 return $a;
411 } else {
412 $a = $this->parseAnswer($answer, $display);
413 return $a;
415 return $ans_set;