Skip to content

Commit

Permalink
fix: return risk level after questionnaire response is submitted
Browse files Browse the repository at this point in the history
This changes the workflow for generating a questionnaire response risk summary.
New workflow:
-  After a questionnaire response is submitted, calculate the scores
-  Based off the score, generate a risk summary
-  Store the risk summary as FHIR RiskAssessment resource
-  Return the risk summary i.e either High Risk / Low Risk (For now)
  • Loading branch information
Salaton committed Feb 9, 2024
1 parent d8e7ede commit c8ab4fd
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 184 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1987,6 +1987,13 @@ func NewFHIRMock() *FHIRMock {
riskAssessment := &domain.FHIRRiskAssessment{
ID: new(string),
Meta: &domain.FHIRMeta{},
Prediction: []domain.FHIRRiskAssessmentPrediction{
{
Outcome: &domain.FHIRCodeableConcept{
Text: "High Risk",
},
},
},
}
return &domain.FHIRRiskAssessmentRelayPayload{
Resource: riskAssessment,
Expand Down
2 changes: 1 addition & 1 deletion pkg/clinical/presentation/graph/clinical.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ extend type Mutation {
questionnaireID: String!
encounterID: String!
input: QuestionnaireResponseInput!
): QuestionnaireResponse!
): String!

# Diagnostic Report
recordMammographyResult(input: DiagnosticReportInput!): DiagnosticReport!
Expand Down
2 changes: 1 addition & 1 deletion pkg/clinical/presentation/graph/clinical.resolvers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 5 additions & 27 deletions pkg/clinical/presentation/graph/generated/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

99 changes: 73 additions & 26 deletions pkg/clinical/usecases/clinical/questionnaire_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package clinical
import (
"context"
"fmt"
"time"

"github.com/mitchellh/mapstructure"
"github.com/savannahghi/clinical/pkg/clinical/application/common"
Expand All @@ -12,21 +13,21 @@ import (
)

// CreateQuestionnaireResponse creates a questionnaire response
func (u *UseCasesClinicalImpl) CreateQuestionnaireResponse(ctx context.Context, questionnaireID string, encounterID string, input dto.QuestionnaireResponse) (*dto.QuestionnaireResponse, error) {
func (u *UseCasesClinicalImpl) CreateQuestionnaireResponse(ctx context.Context, questionnaireID string, encounterID string, input dto.QuestionnaireResponse) (string, error) {
questionnaireResponse := &domain.FHIRQuestionnaireResponse{}

err := mapstructure.Decode(input, questionnaireResponse)
if err != nil {
return nil, err
return "", err
}

encounter, err := u.infrastructure.FHIR.GetFHIREncounter(ctx, encounterID)
if err != nil {
return nil, err
return "", err
}

if encounter.Resource.Status == domain.EncounterStatusEnumFinished {
return nil, fmt.Errorf("cannot create a questionnaire response in a finished encounter")
return "", fmt.Errorf("cannot create a questionnaire response in a finished encounter")
}

patientID := encounter.Resource.Subject.ID
Expand All @@ -49,7 +50,7 @@ func (u *UseCasesClinicalImpl) CreateQuestionnaireResponse(ctx context.Context,

tags, err := u.GetTenantMetaTags(ctx)
if err != nil {
return nil, err
return "", err
}

questionnaireResponse.Meta = &domain.FHIRMetaInput{
Expand All @@ -61,29 +62,29 @@ func (u *UseCasesClinicalImpl) CreateQuestionnaireResponse(ctx context.Context,

resp, err := u.infrastructure.FHIR.CreateFHIRQuestionnaireResponse(ctx, questionnaireResponse)
if err != nil {
return nil, err
return "", err
}

output := &dto.QuestionnaireResponse{}

err = mapstructure.Decode(resp, output)
if err != nil {
return nil, err
return "", err
}

// TODO: This will affect the API performance. Optimize it
err = u.generateQuestionnaireReviewSummary(
riskLevel, err := u.generateQuestionnaireReviewSummary(
ctx,
questionnaireID,
*resp.ID,
encounterID,
encounter,
output,
)
if err != nil {
return nil, err
return "", err
}

return output, nil
return riskLevel, nil
}

// generateQuestionnaireReviewSummary takes a questionnaire response and
Expand All @@ -96,13 +97,13 @@ func (u *UseCasesClinicalImpl) CreateQuestionnaireResponse(ctx context.Context,
func (u *UseCasesClinicalImpl) generateQuestionnaireReviewSummary(
ctx context.Context,
questionnaireID,
questionnaireResponseID,
encounterID string,
questionnaireResponseID string,
encounterID *domain.FHIREncounterRelayPayload,
questionnaireResponse *dto.QuestionnaireResponse,
) error {
) (string, error) {
questionnaire, err := u.infrastructure.FHIR.GetFHIRQuestionnaire(ctx, questionnaireID)
if err != nil {
return err
return "", err
}

switch *questionnaire.Resource.Name {
Expand Down Expand Up @@ -132,42 +133,42 @@ func (u *UseCasesClinicalImpl) generateQuestionnaireReviewSummary(

switch {
case totalScore >= 2:
err := u.recordRiskAssessment(
riskLevel, err := u.recordRiskAssessment(
ctx,
encounterID,
questionnaireResponseID,
common.HighRiskCIELCode,
"High Risk",
)
if err != nil {
return err
return riskLevel, err
}

case totalScore < 2:
err := u.recordRiskAssessment(
riskLevel, err := u.recordRiskAssessment(
ctx,
encounterID,
questionnaireResponseID,
common.LowRiskCIELCode,
"Low Risk",
)
if err != nil {
return err
return riskLevel, err
}
}
default:
return fmt.Errorf("questionnaire does not exist")
return "", fmt.Errorf("questionnaire does not exist")
}

return nil
return "", nil
}

func (u *UseCasesClinicalImpl) recordRiskAssessment(
ctx context.Context,
encounterID,
encounter *domain.FHIREncounterRelayPayload,
questionnaireResponseID string,
outcomeCode, outcomeDisplay string,
) error {
) (string, error) {
CIELTerminologySystem := scalarutils.URI(common.CIELTerminologySystem)
codingCode := scalarutils.Code(outcomeCode)

Expand All @@ -182,12 +183,58 @@ func (u *UseCasesClinicalImpl) recordRiskAssessment(
Text: outcomeDisplay,
}

_, err := u.RecordRiskAssessment(ctx, encounterID, questionnaireResponseID, outcome)
patientID := encounter.Resource.Subject.ID
patientReference := fmt.Sprintf("Patient/%s", *patientID)

encounterReference := fmt.Sprintf("Encounter/%s", *encounter.Resource.ID)

questionnaireResponseReference := fmt.Sprintf("QuestionnaireResponse/%s", questionnaireResponseID)

instant := scalarutils.Instant(time.Now().Format(time.RFC3339))

riskAssessment := domain.FHIRRiskAssessmentInput{
Status: domain.ObservationStatusEnumFinal,
Code: &domain.FHIRCodeableConceptInput{},
Subject: domain.FHIRReferenceInput{
ID: encounter.Resource.Subject.ID,
Reference: &patientReference,
Display: encounter.Resource.Subject.Display,
},

Encounter: &domain.FHIRReferenceInput{
ID: encounter.Resource.ID,
Reference: &encounterReference,
},
OccurrenceDateTime: (*string)(&instant),
Prediction: []domain.FHIRRiskAssessmentPrediction{
{
Outcome: &outcome,
},
},
Basis: []domain.FHIRReferenceInput{
{
Reference: &questionnaireResponseReference,
},
},
}

tags, err := u.GetTenantMetaTags(ctx)
if err != nil {
return err
return "", err
}

return nil
riskAssessment.Meta = &domain.FHIRMetaInput{
Tag: tags,
}

assessment, err := u.RecordRiskAssessment(ctx, &riskAssessment)
if err != nil {
return "", err
}

riskLevel := assessment.Prediction[0].Outcome.Text

return riskLevel, nil
}

// GetQuestionnaireResponseRiskLevel fetches the risk level associated with a questionnaire response. This is based off the scoring
Expand Down
35 changes: 35 additions & 0 deletions pkg/clinical/usecases/clinical/questionnaire_response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,24 @@ func TestUseCasesClinicalImpl_CreateQuestionnaireResponse(t *testing.T) {
},
wantErr: true,
},
{
name: "Sad Case - non-existent fhir questionnaire",
args: args{
ctx: context.Background(),
encounterID: gofakeit.UUID(),
questionnaireID: gofakeit.UUID(),
},
wantErr: true,
},
{
name: "Sad Case - fail to get tenant meta tags",
args: args{
ctx: context.Background(),
encounterID: gofakeit.UUID(),
questionnaireID: gofakeit.UUID(),
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -305,6 +323,23 @@ func TestUseCasesClinicalImpl_CreateQuestionnaireResponse(t *testing.T) {
}
}

if tt.name == "Sad Case - non-existent fhir questionnaire" {
randomName := gofakeit.BeerName()
fakeFHIR.MockGetFHIRQuestionnaireFn = func(ctx context.Context, id string) (*domain.FHIRQuestionnaireRelayPayload, error) {
return &domain.FHIRQuestionnaireRelayPayload{
Resource: &domain.FHIRQuestionnaire{
Name: &randomName,
},
}, nil
}
}

if tt.name == "Sad Case - fail to get tenant meta tags" {
fakeExt.MockGetTenantIdentifiersFn = func(ctx context.Context) (*dto.TenantIdentifiers, error) {
return nil, fmt.Errorf("failed to get tenant identifiers")
}
}

if tt.name == "Sad Case - Fail to record risk assessment - Low Risk" {
setupMockFHIRFunctions(fakeFHIR, 0)
}
Expand Down
Loading

0 comments on commit c8ab4fd

Please sign in to comment.