Skip to content

Commit

Permalink
refactor: setup composition when starting an encounter (#402)
Browse files Browse the repository at this point in the history
Signed-off-by: Kathurima Kimathi <kathurimakimathi415@gmail.com>
  • Loading branch information
KathurimaKimathi authored and Salaton committed Mar 27, 2024
1 parent 8d6d629 commit 9aba957
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 74 deletions.
37 changes: 24 additions & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
# Use the official Golang image to create a build artifact.
# This is based on Debian and sets the GOPATH to /go.
# https://hub.docker.com/_/golang
FROM golang:1.21 as builder
FROM golang:1.22-bullseye as builder

# Create and change to the app directory.
WORKDIR /app

# Install wkhtmltopdf in the builder stage.
RUN apt-get update && apt-get install -y wkhtmltopdf && apt-get clean

# Copy go.sum/go.mod and warm up the module cache.
COPY go.* ./
RUN go mod download
Expand All @@ -22,22 +18,37 @@ COPY . .
# Build the binary.
RUN CGO_ENABLED=0 GOOS=linux go build -v -o server github.com/savannahghi/clinical

# Use the official Alpine image for a lean production container.
FROM alpine:3 as production

# Install ca-certificates and other runtime dependencies for wkhtmltopdf.
RUN apk add --no-cache ca-certificates
FROM debian:bullseye-slim as production

# Install necessary libraries for wkhtmltopdf.
RUN apt-get update && apt-get install -y --no-install-recommends \
wkhtmltopdf \
libstdc++6 \
libx11-6 \
libxrender1 \
libxext6 \
libfontconfig1 \
fonts-dejavu \
fonts-droid-fallback \
fonts-freefont-ttf \
fonts-liberation \
libqt5webkit5 \
libqt5widgets5 \
libqt5gui5 \
libqt5core5a \
libqt5network5 \
ca-certificates \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Copy the wkhtmltopdf binary and related files from the builder stage to the production image.
COPY --from=builder /usr/bin/wkhtmltopdf /usr/local/bin/

# 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
# Set the working directory to where your binary and templates are.
WORKDIR /app

# Run the web service on container startup.
Expand Down
7 changes: 5 additions & 2 deletions pkg/clinical/application/common/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,11 @@ const (
// LOINCExamination defines LOINC Examination note terminology code
LOINCExamination = "29545-1"

// LOINCPLANOFCARE defines LOINC Plan of care note terminology code
LOINCPLANOFCARE = "18776-5"
// LOINCPlanOfCare defines LOINC Plan of care note terminology code
LOINCPlanOfCare = "18776-5"

// LOINCProviderUnspecifiedProgressNote defines LOINC Provider unspecified progress note terminology code
LOINCProviderUnspecifiedProgressNote = "11506-3"

// ColposcopyCIELTerminologyCode is the terminology code for colposcopy findings
ColposcopyCIELTerminologyCode = "162816"
Expand Down
13 changes: 7 additions & 6 deletions pkg/clinical/application/dto/enums.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,13 @@ const (
type CompositionCategory string

const (
AssessmentAndPlan CompositionCategory = "ASSESSMENT_PLAN"
HistoryOfPresentingIllness CompositionCategory = "HISTORY_OF_PRESENTING_ILLNESS"
SocialHistory CompositionCategory = "SOCIAL_HISTORY"
FamilyHistory CompositionCategory = "FAMILY_HISTORY"
Examination CompositionCategory = "EXAMINATION"
PlanOfCare CompositionCategory = "PLAN_OF_CARE"
AssessmentAndPlan CompositionCategory = "ASSESSMENT_PLAN"
HistoryOfPresentingIllness CompositionCategory = "HISTORY_OF_PRESENTING_ILLNESS"
SocialHistory CompositionCategory = "SOCIAL_HISTORY"
FamilyHistory CompositionCategory = "FAMILY_HISTORY"
Examination CompositionCategory = "EXAMINATION"
PlanOfCare CompositionCategory = "PLAN_OF_CARE"
ProviderUnspecifiedProgressNote CompositionCategory = "PROGRESS_NOTE"
)

// Type enum represents type composition attribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,14 @@ func NewFHIRMock() *FHIRMock {
return &domain.FHIREncounterRelayPayload{
Resource: &domain.FHIREncounter{
ID: &resourceID,
Meta: &domain.FHIRMeta{
Source: resourceID,
Tag: []domain.FHIRCoding{
{
Code: (*scalarutils.Code)(&resourceID),
},
},
},
},
}, nil
},
Expand Down
75 changes: 22 additions & 53 deletions pkg/clinical/usecases/clinical/composition.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,31 +54,12 @@ func (c *UseCasesClinicalImpl) CreateComposition(ctx context.Context, input dto.

id := uuid.New().String()

var compositionCategoryCode string

switch input.Category {
case "ASSESSMENT_PLAN":
compositionCategoryCode = common.LOINCAssessmentPlanCode
case "HISTORY_OF_PRESENTING_ILLNESS":
compositionCategoryCode = common.LOINCHistoryOfPresentingIllness
case "SOCIAL_HISTORY":
compositionCategoryCode = common.LOINCSocialHistory
case "FAMILY_HISTORY":
compositionCategoryCode = common.LOINCFamilyHistory
case "EXAMINATION":
compositionCategoryCode = common.LOINCExamination
case "PLAN_OF_CARE":
compositionCategoryCode = common.LOINCPLANOFCARE
default:
return nil, fmt.Errorf("category is needed")
}

compositionCategoryConcept, err := c.GetConcept(ctx, dto.TerminologySourceLOINC, compositionCategoryCode)
compositionCategoryCode, err := c.mapCategoryEnumToCode(input.Category)
if err != nil {
return nil, err
}

compositionTypeConcept, err := c.GetConcept(ctx, dto.TerminologySourceLOINC, common.LOINCProgressNoteCode)
compositionConcept, err := c.mapCompositionConcepts(ctx, compositionCategoryCode, common.LOINCProgressNoteCode)
if err != nil {
return nil, err
}
Expand All @@ -91,24 +72,24 @@ func (c *UseCasesClinicalImpl) CreateComposition(ctx context.Context, input dto.
Type: &domain.FHIRCodeableConceptInput{
Coding: []*domain.FHIRCodingInput{
{
System: (*scalarutils.URI)(&compositionTypeConcept.URL),
Code: scalarutils.Code(compositionTypeConcept.ID),
Display: compositionTypeConcept.DisplayName,
System: (*scalarutils.URI)(&compositionConcept.CompositionTypeConcept.URL),
Code: scalarutils.Code(compositionConcept.CompositionTypeConcept.ID),
Display: compositionConcept.CompositionTypeConcept.DisplayName,
},
},
Text: compositionTypeConcept.DisplayName,
Text: compositionConcept.CompositionTypeConcept.DisplayName,
},
Category: []*domain.FHIRCodeableConceptInput{
{
ID: &id,
Coding: []*domain.FHIRCodingInput{
{
System: (*scalarutils.URI)(&compositionCategoryConcept.URL),
Code: scalarutils.Code(compositionCategoryConcept.ID),
Display: compositionCategoryConcept.DisplayName,
System: (*scalarutils.URI)(&compositionConcept.CompositionCategoryConcept.URL),
Code: scalarutils.Code(compositionConcept.CompositionCategoryConcept.ID),
Display: compositionConcept.CompositionCategoryConcept.DisplayName,
},
},
Text: compositionCategoryConcept.DisplayName,
Text: compositionConcept.CompositionCategoryConcept.DisplayName,
},
},
Subject: &domain.FHIRReferenceInput{
Expand All @@ -132,18 +113,18 @@ func (c *UseCasesClinicalImpl) CreateComposition(ctx context.Context, input dto.
Section: []*domain.FHIRCompositionSectionInput{
{
ID: &id,
Title: &compositionCategoryConcept.DisplayName,
Title: &compositionConcept.CompositionCategoryConcept.DisplayName,
Code: &domain.FHIRCodeableConceptInput{
ID: &id,
Coding: []*domain.FHIRCodingInput{
{
ID: &id,
System: (*scalarutils.URI)(&compositionCategoryConcept.URL),
Code: scalarutils.Code(compositionCategoryConcept.ID),
Display: compositionCategoryConcept.DisplayName,
System: (*scalarutils.URI)(&compositionConcept.CompositionCategoryConcept.URL),
Code: scalarutils.Code(compositionConcept.CompositionCategoryConcept.ID),
Display: compositionConcept.CompositionCategoryConcept.DisplayName,
},
},
Text: compositionTypeConcept.DisplayName,
Text: compositionConcept.CompositionTypeConcept.DisplayName,
},
Author: []*domain.FHIRReferenceInput{
{
Expand Down Expand Up @@ -178,12 +159,12 @@ func (c *UseCasesClinicalImpl) CreateComposition(ctx context.Context, input dto.
ID: &id,
Coding: []*domain.FHIRCodingInput{
{
System: (*scalarutils.URI)(&compositionCategoryConcept.URL),
Code: scalarutils.Code(compositionCategoryConcept.ID),
Display: compositionCategoryConcept.DisplayName,
System: (*scalarutils.URI)(&compositionConcept.CompositionCategoryConcept.URL),
Code: scalarutils.Code(compositionConcept.CompositionCategoryConcept.ID),
Display: compositionConcept.CompositionCategoryConcept.DisplayName,
},
},
Text: compositionCategoryConcept.DisplayName,
Text: compositionConcept.CompositionCategoryConcept.DisplayName,
},
}

Expand Down Expand Up @@ -332,21 +313,9 @@ func (c *UseCasesClinicalImpl) AppendNoteToComposition(ctx context.Context, id s

organizationRef := fmt.Sprintf("Organization/%s", identifiers.OrganizationID)

var compositionCategoryCode string

switch input.Category {
case "ASSESSMENT_PLAN":
compositionCategoryCode = common.LOINCAssessmentPlanCode
case "HISTORY_OF_PRESENTING_ILLNESS":
compositionCategoryCode = common.LOINCHistoryOfPresentingIllness
case "SOCIAL_HISTORY":
compositionCategoryCode = common.LOINCSocialHistory
case "FAMILY_HISTORY":
compositionCategoryCode = common.LOINCFamilyHistory
case "EXAMINATION":
compositionCategoryCode = common.LOINCExamination
case "PLAN_OF_CARE":
compositionCategoryCode = common.LOINCPLANOFCARE
compositionCategoryCode, err := c.mapCategoryEnumToCode(input.Category)
if err != nil {
return nil, err
}

compositionCategoryConcept, err := c.GetConcept(ctx, dto.TerminologySourceLOINC, compositionCategoryCode)
Expand Down
60 changes: 60 additions & 0 deletions pkg/clinical/usecases/clinical/composition_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package clinical

import (
"context"
"fmt"

"github.com/savannahghi/clinical/pkg/clinical/application/common"
"github.com/savannahghi/clinical/pkg/clinical/application/dto"
"github.com/savannahghi/clinical/pkg/clinical/domain"
)

// CompositionConcept is used to map composition concepts
type CompositionConcept struct {
CompositionCategoryConcept *domain.Concept
CompositionTypeConcept *domain.Concept
}

// mapCompositionConcepts composes a unified representation of composition concepts and types into a single model
func (c *UseCasesClinicalImpl) mapCompositionConcepts(ctx context.Context, compositionCategoryCode, conceptID string) (*CompositionConcept, error) {
compositionCategoryConcept, err := c.GetConcept(ctx, dto.TerminologySourceLOINC, compositionCategoryCode)
if err != nil {
return nil, err
}

compositionTypeConcept, err := c.GetConcept(ctx, dto.TerminologySourceLOINC, conceptID)
if err != nil {
return nil, err
}

return &CompositionConcept{
CompositionCategoryConcept: compositionCategoryConcept,
CompositionTypeConcept: compositionTypeConcept,
}, nil
}

// mapCategoryEnumToCode is used to map various composition categories to respective LOINC codes
func (*UseCasesClinicalImpl) mapCategoryEnumToCode(category dto.CompositionCategory) (string, error) {
var compositionCategoryCode string

switch category {
case "ASSESSMENT_PLAN":
compositionCategoryCode = common.LOINCAssessmentPlanCode
case "HISTORY_OF_PRESENTING_ILLNESS":
compositionCategoryCode = common.LOINCHistoryOfPresentingIllness
case "SOCIAL_HISTORY":
compositionCategoryCode = common.LOINCSocialHistory
case "FAMILY_HISTORY":
compositionCategoryCode = common.LOINCFamilyHistory
case "EXAMINATION":
compositionCategoryCode = common.LOINCExamination
case "PLAN_OF_CARE":
compositionCategoryCode = common.LOINCPlanOfCare
case "PROGRESS_NOTE":
compositionCategoryCode = common.LOINCProviderUnspecifiedProgressNote
default:
return "", fmt.Errorf("category is needed")
}

return compositionCategoryCode, nil
}
Loading

0 comments on commit 9aba957

Please sign in to comment.