Skip to content

Commit

Permalink
feat: generate referral report pdf
Browse files Browse the repository at this point in the history
  • Loading branch information
Salaton committed Mar 26, 2024
1 parent 16c0048 commit b5dd4cf
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 100 deletions.
16 changes: 16 additions & 0 deletions pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go
Original file line number Diff line number Diff line change
Expand Up @@ -2034,3 +2034,19 @@ func (fh StoreImpl) GetFHIRPatientEverything(ctx context.Context, id string, par

return &response, nil
}

// GetFHIRServiceRequest retrieves a FHIR service request using its primary ID
func (fh StoreImpl) GetFHIRServiceRequest(_ context.Context, id string) (*domain.FHIRServiceRequestRelayPayload, error) {
resource := &domain.FHIRServiceRequest{}

err := fh.Dataset.GetFHIRResource(serviceRequestResourceType, id, resource)
if err != nil {
return nil, fmt.Errorf("unable to get %s with ID %s, err: %w", serviceRequestResourceType, id, err)
}

payload := &domain.FHIRServiceRequestRelayPayload{
Resource: resource,
}

return payload, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ type FHIRMock struct {
MockCreateFHIRDiagnosticReportFn func(_ context.Context, input *domain.FHIRDiagnosticReportInput) (*domain.FHIRDiagnosticReport, error)
MockSearchFHIREncounterAllDataFn func(_ context.Context, params map[string]interface{}, tenant dto.TenantIdentifiers, pagination dto.Pagination) (*domain.PagedFHIRResource, error)
MockGetFHIRPatientEverythingFn func(ctx context.Context, id string, params map[string]interface{}) (*domain.PagedFHIRResource, error)
MockGetFHIRServiceRequestFn func(_ context.Context, id string) (*domain.FHIRServiceRequestRelayPayload, error)
}

// NewFHIRMock initializes a new instance of FHIR mock
Expand Down Expand Up @@ -2154,6 +2155,17 @@ func NewFHIRMock() *FHIRMock {
TotalCount: 0,
}, nil
},
MockGetFHIRServiceRequestFn: func(_ context.Context, id string) (*domain.FHIRServiceRequestRelayPayload, error) {
return &domain.FHIRServiceRequestRelayPayload{
Resource: &domain.FHIRServiceRequest{
ID: new(string),
Text: &domain.FHIRNarrative{},
Identifier: []*domain.FHIRIdentifier{},
Subject: &domain.FHIRReference{},
Encounter: &domain.FHIRReference{},
},
}, nil
},
}
}

Expand Down Expand Up @@ -2511,3 +2523,8 @@ func (fh *FHIRMock) SearchFHIREncounterAllData(ctx context.Context, params map[s
func (fh *FHIRMock) GetFHIRPatientEverything(ctx context.Context, id string, params map[string]interface{}) (*domain.PagedFHIRResource, error) {
return fh.MockGetFHIRPatientEverythingFn(ctx, id, params)
}

// GetFHIRServiceRequest mocks the implementation of getting a service request by ID
func (fh *FHIRMock) GetFHIRServiceRequest(ctx context.Context, id string) (*domain.FHIRServiceRequestRelayPayload, error) {
return fh.MockGetFHIRServiceRequestFn(ctx, id)
}
4 changes: 2 additions & 2 deletions pkg/clinical/presentation/rest/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,12 +379,12 @@ func (p PresentationHandlersImpl) ListQuestionnaire(c *gin.Context) {

func (p PresentationHandlersImpl) GenerateReferralReport(c *gin.Context) {
queryParams := c.Request.URL.Query()
patientID := queryParams.Get("patient")
serviceRequestID := queryParams.Get("servicerequest")

c.Header("Content-Type", "application/pdf")
// c.Header("Content-Disposition", "attachment; filename=referral_report.pdf")

err := p.usecases.GenerateReferralReportPDF(c.Request.Context(), patientID)
err := p.usecases.GenerateReferralReportPDF(c.Request.Context(), serviceRequestID)
if err != nil {
jsonErrorResponse(c, http.StatusBadRequest, err)
return
Expand Down
1 change: 1 addition & 0 deletions pkg/clinical/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type FHIRServiceRequest interface {
SearchFHIRServiceRequest(ctx context.Context, params map[string]interface{}, tenant dto.TenantIdentifiers, pagination dto.Pagination) (*domain.FHIRServiceRequestRelayConnection, error)
CreateFHIRServiceRequest(ctx context.Context, input domain.FHIRServiceRequestInput) (*domain.FHIRServiceRequestRelayPayload, error)
DeleteFHIRServiceRequest(ctx context.Context, id string) (bool, error)
GetFHIRServiceRequest(ctx context.Context, id string) (*domain.FHIRServiceRequestRelayPayload, error)
}
type FHIRMedicationRequest interface {
SearchFHIRMedicationRequest(ctx context.Context, params map[string]interface{}, tenant dto.TenantIdentifiers, pagination dto.Pagination) (*domain.FHIRMedicationRequestRelayConnection, error)
Expand Down
72 changes: 44 additions & 28 deletions pkg/clinical/usecases/clinical/referral_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/SebastiaanKlippert/go-wkhtmltopdf"
)

// Define your data structures
type Patient struct {
Name string
EmpowerID string
Expand Down Expand Up @@ -77,9 +76,25 @@ type TemplateData struct {
Footer Footer
}

// GenerateReferralReportPDF generates a PDF for a referral report given a patientID.
func (c *UseCasesClinicalImpl) GenerateReferralReportPDF(ctx context.Context, patientID string) error {
patient, err := c.infrastructure.FHIR.GetFHIRPatient(ctx, patientID)
// GenerateReferralReportPDF generates a PDF report for a given referral.
//
// The serviceRequestID is unique to each ServiceRequest resource, which,
// according to FHIR standards, is how referrals are represented. In FHIR, a referral
// is a specific type of ServiceRequest, which typically contains details such
// as the requester, the patient, the requested service, and other clinical information.
//
// By leveraging the serviceRequestID, this function retrieves the associated ServiceRequest
// from the FHIR server. It then extracts relevant data, including patient and encounter
// information, to construct a comprehensive referral report. The report is formatted
// as a PDF, making it suitable for clinical review, record-keeping, or sharing with
// other healthcare professionals.
func (c *UseCasesClinicalImpl) GenerateReferralReportPDF(ctx context.Context, serviceRequestID string) error {
serviceRequest, err := c.infrastructure.FHIR.GetFHIRServiceRequest(ctx, serviceRequestID)
if err != nil {
return err
}

patient, err := c.infrastructure.FHIR.GetFHIRPatient(ctx, *serviceRequest.Resource.Subject.ID)
if err != nil {
return err
}
Expand All @@ -95,47 +110,48 @@ func (c *UseCasesClinicalImpl) GenerateReferralReportPDF(ctx context.Context, pa
Sex: patient.Resource.Gender.String(),
}

var facilityID string
if patient.Resource.ManagingOrganization != nil && patient.Resource.ManagingOrganization.ID != nil {
facilityID = *patient.Resource.ManagingOrganization.ID
} else {
tenantIdentifiers, err := c.infrastructure.BaseExtension.GetTenantIdentifiers(ctx)
if err != nil {
return err
var referredFacilityName, referredSpecialistName string

for _, extension := range serviceRequest.Resource.Extension {
switch extension.URL {
case "http://savannahghi.org/fhir/StructureDefinition/referred-facility":
for _, ext := range extension.Extension {
if ext.URL == "facilityName" {
referredFacilityName = ext.ValueString
}
}

case "http://savannahghi.org/fhir/StructureDefinition/referred-specialist":
for _, ext := range extension.Extension {
if ext.URL == "specialistName" {
referredSpecialistName = ext.ValueString
}
}
}
facilityID = tenantIdentifiers.FacilityID
}

facility, err := c.infrastructure.FHIR.GetFHIROrganization(ctx, facilityID)
if err != nil {
return err
}

data := TemplateData{
Date: time.Now().Format("20th March 2024"),
Time: time.Now().Format("10:00 AM"),
Patient: patientData,
// NextOfKin: NextOfKin{},
Date: time.Now().Format("Monday Jan 2"),
Time: time.Now().Format("15:04"),
Patient: patientData,
NextOfKin: NextOfKin{},

// TODO: Capture facility details from api
Facility: Facility{
Name: *facility.Resource.Name,
Contact: *facility.Resource.Telecom[0].Value,
Name: referredFacilityName,
},
Referral: Referral{
// TODO: Get the reason from the API
Reason: "Further Testing",
},
MedicalHistory: MedicalHistory{Procedure: "Cervical cancer screening", Medication: "None", ReferralNotes: "Patient complains of severe abdominal pain and intermittent bleeding.", Tests: []Test{{Name: "VIA", Results: "Positive", Date: "13th May 2024"}}},
MedicalHistory: MedicalHistory{Procedure: "Screening", Medication: "None", ReferralNotes: "Patient complains of severe abdominal pain and intermittent bleeding.", Tests: []Test{{Name: "VIA", Results: "Positive", Date: "13th May 2024"}}},
ReferredBy: ReferredBy{
Name: "Charles Muchogo",
Name: referredSpecialistName,
Designation: "Doctor",
Phone: "+254711990990",
Signature: "",
},
Footer: Footer{
Phone: *facility.Resource.Telecom[0].Value,
},
Footer: Footer{},
}

tmpl, err := template.ParseFiles("pkg/clinical/usecases/clinical/referral_report_template.html")
Expand Down
94 changes: 24 additions & 70 deletions pkg/clinical/usecases/clinical/referral_report_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,39 @@
<meta charset="UTF-8">
<title>Referral Report</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f4f4f4;
}
.report-container {
background-color: #ffffff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.detail-section {
background-color: #fff;
margin-bottom: 20px;
padding: 15px;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.detail-section h2 {
color: #333;
margin-bottom: 10px;
}
.detail {
margin-bottom: 5px;
}
.detail strong {
color: #333;
}
body { font-family: Arial, sans-serif; margin: 20px; font-size: 14px; }
.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
.header-logo, .header-title { color: #800080; font-weight: bold; }
.header-logo { font-size: 24px; }
.header-details { text-align: right; }
.header-details div { margin: 5px 0; }
.report-title { text-align: center; font-size: 30px; font-weight: bold; margin: 20px 0; }
.detail-section { background-color: #f2f2f2; padding: 20px; margin-bottom: 20px; border-radius: 8px; }
.detail-section h2 { margin: 0 0 20px 0; }
.detail { margin-bottom: 10px; }
.detail strong { margin-right: 5px; }
.footer {
text-align: center;
padding: 10px;
color: white;
background-color: #4D184D;
margin-top: 30px;
}
/* Additional styles for header and layout */
.report-header {
background-color: #ffffff;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
margin-bottom: 30px;
}
.header-logo {
width: 100px;
}
.header-details {
text-align: right;
color: #333;
font-size: 16px;
}
.header-details div {
margin-bottom: 5px;
}
.header-title {
color: #4D184D;
font-size: 24px;
margin-left: 20px;
}
.report-title {
text-align: center;
margin-bottom: 20px;
font-size: 24px;
color: #4D184D;
}
.separator {
border-bottom: 2px solid #4D184D;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="report-header">
<!-- <img src="logo.png" alt="Empower Coast General Hospital" class="header-logo"> -->
<div class="header-title">Empower Coast General Hospital</div>
<div class="header">
<div>
<span class="header-logo">Empower</span>
<span class="header-title">Empower Coast General Hospital</span>
</div>
<div class="header-details">
<div>Date: {{.Date}}</div>
<div>Time: {{.Time}}</div>
<div>Reason: {{.Reason}}</div>
</div>
</div>
<div class="separator"></div>

<div class="report-title">Referral Report</div>

<div class="report-container">
Expand All @@ -103,6 +52,7 @@ <h2>Patient details</h2>
{{if .Patient.Sex}}<div class="detail"><strong>Sex:</strong> {{.Patient.Sex}}</div>{{end}}
</div>
{{end}}

{{if .NextOfKin}}
<div class="detail-section">
<h2>Next of kin details</h2>
Expand All @@ -111,6 +61,7 @@ <h2>Next of kin details</h2>
{{if .NextOfKin.PhoneNumber}}<div class="detail"><strong>Phone number:</strong> {{.NextOfKin.PhoneNumber}}</div>{{end}}
</div>
{{end}}

{{if .Facility}}
<div class="detail-section">
<h2>Receiving facility details</h2>
Expand All @@ -119,12 +70,14 @@ <h2>Receiving facility details</h2>
{{if .Facility.Contact}}<div class="detail"><strong>Hospital contact:</strong> {{.Facility.Contact}}</div>{{end}}
</div>
{{end}}

{{if .Referral.Reason}}
<div class="detail-section">
<h2>Referral reason</h2>
<div class="detail">{{.Referral.Reason}}</div>
<div class="detail"><strong>Reason for referral:</strong> {{.Referral.Reason}}</div>
</div>
{{end}}

{{if .MedicalHistory}}
<div class="detail-section">
<h2>Medical history</h2>
Expand All @@ -138,6 +91,7 @@ <h2>Medical history</h2>
{{end}}
</div>
{{end}}

{{if .ReferredBy}}
<div class="detail-section">
<h2>Referred by</h2>
Expand All @@ -156,4 +110,4 @@ <h2>Referred by</h2>
</div>
{{end}}
</body>
</html>
</html>

0 comments on commit b5dd4cf

Please sign in to comment.