Skip to content

Commit

Permalink
feat: persist patient referral form as FHIR document reference
Browse files Browse the repository at this point in the history
Signed-off-by: Kathurima Kimathi <kathurimakimathi415@gmail.com>
  • Loading branch information
KathurimaKimathi committed Apr 9, 2024
1 parent b793b1b commit bd69e09
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 28 deletions.
3 changes: 0 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# Copy the Go binary to the production image from the builder stage.
COPY --from=builder /app/server /server

# Ensure your templates directory is correctly copied into the Docker image.
COPY --from=builder /app/templates /app/templates

# Set the working directory to where your binary and templates are.
WORKDIR /app

Expand Down
3 changes: 3 additions & 0 deletions pkg/clinical/application/common/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ const (
// BreastExaminationCIELTerminologySystem is the terminology code used to represent breast examination concept.
// This is more a more general concept code.
BreastExaminationCIELTerminologySystem = "162825"

// ReferralNoteLOINCTerminologySystem is the system code used to represent referral note concept in loinc terminology system
ReferralNoteLOINCTerminologySystem = "57133-1"
)

// DefaultIdentifier assigns a patient a code to function as their
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
package utils

const ReferralFormTemplate = `
<!DOCTYPE html>
<html lang="en">
<head>
Expand Down Expand Up @@ -298,4 +301,4 @@ <h2>Referred by</h2>
{{end}}
</body>
</html>
`
4 changes: 3 additions & 1 deletion pkg/clinical/domain/document_reference.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package domain

import "github.com/savannahghi/scalarutils"

// FHIRDocumentReference represents a reference to a document of any kind for any purpose.
// It provides metadata about the document so that the document can be discovered and managed.
// The scope of a document is any seralized object with a mime-type, so includes formal patient centric documents (CDA), cliical notes, scanned paper, and non-patient centric documents like policy text.
Expand Down Expand Up @@ -78,7 +80,7 @@ type FHIRDocumentReferenceInput struct {
Type *FHIRCodeableConceptInput `json:"type,omitempty"`
Category []FHIRCodeableConceptInput `json:"category,omitempty"`
Subject *FHIRReferenceInput `json:"subject,omitempty"`
Date *string `json:"date,omitempty"`
Date *scalarutils.Instant `json:"date,omitempty"`
Author []FHIRReferenceInput `json:"author,omitempty"`
Authenticator *FHIRReferenceInput `json:"authenticator,omitempty"`
Custodian *FHIRReferenceInput `json:"custodian,omitempty"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ type FHIRMock struct {
MockGetFHIRPatientEverythingFn func(ctx context.Context, id string, params map[string]interface{}) (*domain.PagedFHIRResource, error)
MockGetFHIRServiceRequestFn func(_ context.Context, id string) (*domain.FHIRServiceRequestRelayPayload, error)
MockCreateFHIRSubscriptionFn func(_ context.Context, subscription *domain.FHIRSubscriptionInput) (*domain.FHIRSubscription, error)
MockCreateFHIRDocumentReferenceFn func(ctx context.Context, subscription *domain.FHIRSubscriptionInput) (*domain.FHIRSubscription, error)
MockCreateFHIRDocumentReferenceFn func(ctx context.Context, documentReference *domain.FHIRDocumentReferenceInput) (*domain.FHIRDocumentReference, error)
}

// NewFHIRMock initializes a new instance of FHIR mock
Expand Down Expand Up @@ -2263,24 +2263,33 @@ func NewFHIRMock() *FHIRMock {
Channel: domain.FHIRSubscriptionChannel{},
}, nil
},
MockCreateFHIRDocumentReferenceFn: func(ctx context.Context, subscription *domain.FHIRSubscriptionInput) (*domain.FHIRSubscription, error) {
resourceID := uuid.New().String()
return &domain.FHIRSubscription{
ID: &resourceID,
MockCreateFHIRDocumentReferenceFn: func(ctx context.Context, documentReference *domain.FHIRDocumentReferenceInput) (*domain.FHIRDocumentReference, error) {
resourceID := gofakeit.UUID()
return &domain.FHIRDocumentReference{
ID: resourceID,
Meta: &domain.FHIRMeta{},
ImplicitRules: new(string),
Language: new(string),
Text: &domain.FHIRNarrative{},
Extension: []*domain.Extension{},
ModifierExtension: []*domain.Extension{},
Identifier: []*domain.FHIRIdentifier{},
Extension: []domain.FHIRExtension{},
ModifierExtension: []domain.FHIRExtension{},
MasterIdentifier: &domain.FHIRIdentifier{},
Identifier: []domain.FHIRIdentifier{},
Status: "",
Contact: []domain.FHIRContactPoint{},
End: new(string),
Reason: "",
Criteria: "",
Error: new(string),
Channel: domain.FHIRSubscriptionChannel{},
Type: &domain.FHIRCodeableConcept{
ID: &resourceID,
},
Category: []domain.FHIRCodeableConcept{},
Subject: &domain.FHIRReference{},
Date: new(string),
Author: []domain.FHIRReference{},
Authenticator: &domain.FHIRReference{},
Custodian: &domain.FHIRReference{},
RelatesTo: []domain.FHIRDocumentReferenceRelatesTo{},
Description: "",
SecurityLabel: []domain.FHIRCodeableConcept{},
Content: []domain.FHIRDocumentReferenceContent{},
Context: &domain.FHIRDocumentReferenceContext{},
}, nil
},
}
Expand Down Expand Up @@ -2652,6 +2661,6 @@ func (fh *FHIRMock) CreateFHIRSubscription(ctx context.Context, subscription *do
}

// CreateFHIRDocumentReference mocks the implementation of creating a FHIR document reference
func (fh *FHIRMock) CreateFHIRDocumentReference(ctx context.Context, subscription *domain.FHIRSubscriptionInput) (*domain.FHIRSubscription, error) {
return fh.MockCreateFHIRSubscriptionFn(ctx, subscription)
func (fh *FHIRMock) CreateFHIRDocumentReference(ctx context.Context, documentReference *domain.FHIRDocumentReferenceInput) (*domain.FHIRDocumentReference, error) {
return fh.MockCreateFHIRDocumentReferenceFn(ctx, documentReference)
}
1 change: 1 addition & 0 deletions pkg/clinical/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type FHIR interface {
FHIRRiskAssessment
FHIRDiagnosticReport
FHIRSubscription
FHIRDocumentReference
}

type FHIROrganization interface {
Expand Down
70 changes: 63 additions & 7 deletions pkg/clinical/usecases/clinical/referral_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import (
"time"

"github.com/SebastiaanKlippert/go-wkhtmltopdf"
"github.com/savannahghi/clinical/pkg/clinical/application/common"
"github.com/savannahghi/clinical/pkg/clinical/application/common/helpers"
"github.com/savannahghi/clinical/pkg/clinical/application/dto"
"github.com/savannahghi/clinical/pkg/clinical/application/utils"
"github.com/savannahghi/clinical/pkg/clinical/domain"
"github.com/savannahghi/scalarutils"
)

Expand Down Expand Up @@ -162,14 +165,10 @@ func (c *UseCasesClinicalImpl) GenerateReferralReportPDF(ctx context.Context, se
Footer: Footer{},
}

tmpl, err := template.ParseFiles("templates/referral_report_template.html")
if err != nil {
utils.ReportErrorToSentry(err)
return nil, err
}

var htmlBuffer bytes.Buffer

tmpl := template.Must(template.New("ReferralFormTemplate").Parse(utils.ReferralFormTemplate))

err = tmpl.Execute(&htmlBuffer, data)
if err != nil {
utils.ReportErrorToSentry(err)
Expand Down Expand Up @@ -197,7 +196,64 @@ func (c *UseCasesClinicalImpl) GenerateReferralReportPDF(ctx context.Context, se
currentTime := time.Now().Format("20060102T150405")
filename := fmt.Sprintf("%s_%s.pdf", patientData.Name, currentTime)

_, err = c.infrastructure.Upload.UploadMedia(ctx, filename, bytes.NewReader(pdfBytes), "")
result, err := c.infrastructure.Upload.UploadMedia(ctx, filename, bytes.NewReader(pdfBytes), "")
if err != nil {
utils.ReportErrorToSentry(err)
return nil, err
}

concept, err := c.GetConcept(ctx, dto.TerminologySourceLOINC, common.ReferralNoteLOINCTerminologySystem)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, err
}

finalDocStatus := domain.CompositionStatusEnumFinal
status := domain.DocumentReferenceStatusEnumCurrent
instant := scalarutils.Instant(time.Now().Format(time.RFC3339))
title := fmt.Sprintf("%s's Referral report", serviceRequest.Resource.Subject.Display)
serviceRequestReference := fmt.Sprintf("ServiceRequest/%s", *serviceRequest.Resource.ID)

documentReference := &domain.FHIRDocumentReferenceInput{
Meta: &domain.FHIRMetaInput{},
Identifier: []domain.FHIRIdentifierInput{},
Status: status,
DocStatus: &finalDocStatus,
Type: &domain.FHIRCodeableConceptInput{
Coding: []*domain.FHIRCodingInput{
{
System: (*scalarutils.URI)(&concept.URL),
Code: scalarutils.Code(concept.ID),
Display: concept.DisplayName,
},
},
Text: concept.DisplayName,
},
Subject: &domain.FHIRReferenceInput{
ID: serviceRequest.Resource.Subject.ID,
Reference: serviceRequest.Resource.Subject.Reference,
},
Date: &instant,
Content: []domain.FHIRDocumentReferenceContent{
{
Attachment: domain.FHIRAttachment{
ContentType: (*scalarutils.Code)(&result.ContentType),
URL: (*scalarutils.URL)(&result.SignedURL),
Title: &title,
},
},
},
Context: &domain.FHIRDocumentReferenceContext{
Related: []domain.Reference{
{
Reference: serviceRequestReference,
Type: "ServiceRequest",
},
},
},
}

_, err = c.infrastructure.FHIR.CreateFHIRDocumentReference(ctx, documentReference)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, err
Expand Down
41 changes: 41 additions & 0 deletions pkg/clinical/usecases/clinical/referral_report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package clinical_test
import (
"context"
"fmt"
"io"
"testing"

"github.com/google/uuid"
"github.com/savannahghi/clinical/pkg/clinical/application/dto"
fakeExtMock "github.com/savannahghi/clinical/pkg/clinical/application/extensions/mock"
"github.com/savannahghi/clinical/pkg/clinical/domain"
"github.com/savannahghi/clinical/pkg/clinical/infrastructure"
Expand Down Expand Up @@ -60,6 +62,30 @@ func TestUseCasesClinicalImpl_GenerateReferralReportPDF(t *testing.T) {
},
wantErr: true,
},
{
name: "Sad Case - unable to upload media",
args: args{
ctx: ctx,
serviceRequestID: uuid.New().String(),
},
wantErr: true,
},
{
name: "Sad Case - unable to create FHIR document reference",
args: args{
ctx: ctx,
serviceRequestID: uuid.New().String(),
},
wantErr: true,
},
{
name: "Sad Case - unable to get terminology concept",
args: args{
ctx: ctx,
serviceRequestID: uuid.New().String(),
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -84,6 +110,21 @@ func TestUseCasesClinicalImpl_GenerateReferralReportPDF(t *testing.T) {
return nil, fmt.Errorf("failed to get patient")
}
}
if tt.name == "Sad Case - unable to upload media" {
fakeUpload.MockUploadMediaFn = func(ctx context.Context, name string, file io.Reader, contentType string) (*dto.Media, error) {
return nil, fmt.Errorf("failed to upload media")
}
}
if tt.name == "Sad Case - unable to create FHIR document reference" {
fakeFHIR.MockCreateFHIRDocumentReferenceFn = func(ctx context.Context, documentReference *domain.FHIRDocumentReferenceInput) (*domain.FHIRDocumentReference, error) {
return nil, fmt.Errorf("failed to create FHIR document reference")
}
}
if tt.name == "Sad Case - unable to get terminology concept" {
fakeOCL.MockGetConceptFn = func(ctx context.Context, org, source, concept string, includeMappings, includeInverseMappings bool) (*domain.Concept, error) {
return nil, fmt.Errorf("failed to get concept")
}
}

if _, err := c.GenerateReferralReportPDF(tt.args.ctx, tt.args.serviceRequestID); (err != nil) != tt.wantErr {
t.Errorf("UseCasesClinicalImpl.GenerateReferralReportPDF() error = %v, wantErr %v", err, tt.wantErr)
Expand Down

0 comments on commit bd69e09

Please sign in to comment.