*/ public function exportBundle(Assessment $assessment): array { $assessment->loadMissing(['template.questions', 'answers.question', 'patient', 'score']); $questionnaire = $this->questionnaire($assessment); $response = $this->questionnaireResponse($assessment, $questionnaire['url']); return [ 'resourceType' => 'Bundle', 'type' => 'collection', 'timestamp' => now()->toIso8601String(), 'meta' => [ 'tag' => [[ 'system' => 'https://care.ladill.com/fhir/tags', 'code' => 'ladill-care-assessment-export', ]], ], 'entry' => [ ['fullUrl' => $questionnaire['url'], 'resource' => $questionnaire], ['fullUrl' => $response['id'] ? 'urn:uuid:'.$response['id'] : 'QuestionnaireResponse/'.$assessment->uuid, 'resource' => $response], ], ]; } /** * @return array */ public function questionnaire(Assessment $assessment): array { $template = $assessment->template; $url = 'https://care.ladill.com/fhir/Questionnaire/'.($template?->code ?? 'unknown').'/v'.($template?->version ?? 1); $items = []; foreach ($template?->questions ?? [] as $question) { $items[] = $this->questionItem($question); } return [ 'resourceType' => 'Questionnaire', 'id' => ($template?->code ?? 'unknown').'-v'.($template?->version ?? 1), 'url' => $url, 'version' => (string) ($template?->version ?? 1), 'name' => Str::studly($template?->code ?? 'Assessment'), 'title' => $template?->name ?? 'Assessment', 'status' => ($template?->is_active ?? true) ? 'active' : 'retired', 'description' => $template?->description, 'item' => $items, ]; } /** * @return array */ public function questionnaireResponse(Assessment $assessment, ?string $questionnaireUrl = null): array { $assessment->loadMissing(['template.questions', 'answers.question', 'patient', 'score']); $questionnaireUrl ??= $this->questionnaire($assessment)['url']; $answersByQ = $assessment->answers->keyBy('question_id'); $items = []; foreach ($assessment->template?->questions ?? [] as $question) { $answer = $answersByQ->get($question->id); if (! $answer) { continue; } $value = $answer->authoritativeValue($question->answer_type); if ($value === null || $value === '') { continue; } $items[] = [ 'linkId' => $question->code, 'text' => $question->label, 'answer' => $this->fhirAnswers($question, $value), ]; } $status = match ($assessment->status) { Assessment::STATUS_COMPLETED => 'completed', Assessment::STATUS_CANCELLED => 'entered-in-error', default => 'in-progress', }; $resource = [ 'resourceType' => 'QuestionnaireResponse', 'id' => $assessment->uuid, 'questionnaire' => $questionnaireUrl, 'status' => $status, 'authored' => ($assessment->assessed_at ?? $assessment->completed_at ?? $assessment->created_at)?->toIso8601String(), 'subject' => [ 'reference' => 'Patient/'.($assessment->patient?->uuid ?? $assessment->patient_id), 'display' => $assessment->patient?->fullName(), ], 'item' => $items, ]; if ($assessment->score) { $resource['extension'] = [[ 'url' => 'https://care.ladill.com/fhir/StructureDefinition/assessment-score', 'extension' => array_values(array_filter([ ['url' => 'total', 'valueDecimal' => (float) $assessment->score->total_score], $assessment->score->max_score !== null ? ['url' => 'max', 'valueDecimal' => (float) $assessment->score->max_score] : null, $assessment->score->severity_label ? ['url' => 'severity', 'valueString' => $assessment->score->severity_label] : null, ['url' => 'templateCode', 'valueString' => $assessment->score->template_code], ])), ]]; } return $resource; } /** * @return array */ protected function questionItem(AssessmentQuestion $question): array { $type = match ($question->answer_type) { AssessmentQuestion::TYPE_BOOLEAN => 'boolean', AssessmentQuestion::TYPE_INTEGER => 'integer', AssessmentQuestion::TYPE_NUMBER, AssessmentQuestion::TYPE_SCALE, AssessmentQuestion::TYPE_SCORE_ITEM, AssessmentQuestion::TYPE_CALCULATED => 'decimal', AssessmentQuestion::TYPE_DATE => 'date', AssessmentQuestion::TYPE_DATETIME => 'dateTime', AssessmentQuestion::TYPE_MULTI_CHOICE => 'choice', AssessmentQuestion::TYPE_SINGLE_CHOICE => 'choice', default => 'string', }; $item = [ 'linkId' => $question->code, 'text' => $question->label, 'type' => $type, 'required' => (bool) $question->is_required, ]; if ($question->help_text) { $item['prefix'] = $question->help_text; } $choices = $question->options['choices'] ?? null; if (is_array($choices) && $choices !== []) { $item['answerOption'] = array_map(function ($c) { $code = (string) ($c['code'] ?? ''); $label = (string) ($c['label'] ?? $code); return [ 'valueCoding' => [ 'code' => $code, 'display' => $label, ], ]; }, $choices); } return $item; } /** * @return list> */ protected function fhirAnswers(AssessmentQuestion $question, mixed $value): array { return match ($question->answer_type) { AssessmentQuestion::TYPE_BOOLEAN => [['valueBoolean' => (bool) $value]], AssessmentQuestion::TYPE_INTEGER => [['valueInteger' => (int) $value]], AssessmentQuestion::TYPE_NUMBER, AssessmentQuestion::TYPE_SCALE, AssessmentQuestion::TYPE_SCORE_ITEM, AssessmentQuestion::TYPE_CALCULATED => [['valueDecimal' => (float) $value]], AssessmentQuestion::TYPE_DATE => [['valueDate' => $value instanceof \Carbon\Carbon ? $value->toDateString() : (string) $value]], AssessmentQuestion::TYPE_DATETIME => [['valueDateTime' => $value instanceof \Carbon\Carbon ? $value->toIso8601String() : (string) $value]], AssessmentQuestion::TYPE_MULTI_CHOICE => collect(is_array($value) ? $value : [$value]) ->map(fn ($v) => ['valueString' => (string) $v]) ->values() ->all(), AssessmentQuestion::TYPE_SINGLE_CHOICE => [['valueString' => (string) $value]], default => [['valueString' => is_scalar($value) ? (string) $value : json_encode($value)]], }; } }