Files
satusehat-bridging/internal/integration/composition_integration.go
2025-11-24 09:13:08 +07:00

836 lines
20 KiB
Go

package integration
import (
"errors"
"os"
"satusehat-rssa/internal/constant"
"satusehat-rssa/internal/model"
"satusehat-rssa/pkg/httputil"
)
type CompositionInterface interface {
CreateComposition(req model.CompositionRequest) (map[string]interface{}, error)
UpdateComposition(req model.CompositionRequest) (map[string]interface{}, error)
GetCompositionByID(id string) (map[string]interface{}, error)
}
type CompositionRepository struct {
akses *model.Akses
}
// CreateComposition implements CompositionInterface.
func (c *CompositionRepository) CreateComposition(req model.CompositionRequest) (map[string]interface{}, error) {
req.ResourceType = "Composition"
req.Identifier = model.Identifier{
Use: "official",
System: "http://sys-ids.kemkes.go.id/composition/" + os.Getenv("ORGANIZATION_ID"),
Value: os.Getenv("ORGANIZATION_ID"),
}
patient, err := c.setupPatient(&req)
if err != nil {
return nil, err
}
err = c.setupEncounter(&req, patient)
if err != nil {
return nil, err
}
err = c.setupOrganization(&req)
if err != nil {
return nil, err
}
// section setup
if req.Anamnesis == "" {
req.Anamnesis = "default anamnesis"
}
if req.PemeriksaanFisik == "" {
req.PemeriksaanFisik = "default pemeriksaan fisik"
}
if req.PemeriksaanPenunjang == "" {
req.PemeriksaanPenunjang = "default pemeriksaan penunjang"
}
if req.Medikamentosa == "" {
req.Medikamentosa = "default medikamentosa"
}
if req.LanjutanPenatalaksanaan == "" {
req.LanjutanPenatalaksanaan = "default lanjutan penatalaksanaan"
}
req.Section = append(req.Section, model.SectionRequest{
Title: "Anamnesis",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://terminology.kemkes.go.id",
Code: "TK000003",
Display: "Anamnesis",
},
},
},
Text: &model.SectionText{
Status: "additional",
Div: req.Anamnesis,
},
})
allergyId := c.setupAllergyIntollerance(patient)
section := model.SectionRequest{
Title: "Riwayat Alergi",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "18776-5",
Display: "Riwayat Alergi",
},
},
},
}
if allergyId != "" {
section.Entry = []model.Reference{
{Reference: "AllergyIntolerance/" + allergyId},
}
}
req.Section = append(req.Section, section)
/* Observation disabled for Now
req.Section = append(req.Section, model.SectionRequest{
Title: "Pemeriksaan Fisik",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://terminology.kemkes.go.id",
Code: "TK000007",
Display: "Pemeriksaan Fisik",
},
},
},
Section: []model.SectionRequest{
{
Title: "Tanda Vital",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "8716-3",
Display: "Vital signs",
},
},
},
Entry: c.setupObservation(patient),
},
{
Title: "Hasil Pemeriksaan",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://terminology.kemkes.go.id",
Code: "TK000007",
Display: "Pemeriksaan Fisik",
},
},
},
Text: &model.SectionText{
Status: "additional",
Div: req.PemeriksaanFisik,
},
},
},
})
*/
var entry []model.Reference
clinicalId := c.setupClinicalImpression(patient)
goalId := c.setupGoal(patient)
carePlanId := c.setupCarePlan(patient)
if clinicalId != "" {
entry = append(entry, model.Reference{Reference: "ClinicalImpression/" + clinicalId})
}
if goalId != "" {
entry = append(entry, model.Reference{Reference: "Goal/" + goalId})
}
if carePlanId != "" {
entry = append(entry, model.Reference{Reference: "CarePlan/" + carePlanId})
}
req.Section = append(req.Section, model.SectionRequest{
Title: "Perencanaan Perawatan",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "18776-5",
Display: "Plan of care note",
},
},
},
Entry: entry,
})
var entryLab, entryRad []model.Reference
serviceRequestIdLab := c.setupServiceRequestCategory(patient, "LAB")
specimenIdLab := c.setupSpecimenCategory(patient)
diagnosticReportIdLab := c.setupDiagnosticCategory(patient, "LAB")
observationIdLab := c.setupObservationCategory(patient, "LAB")
serviceRequestIdRad := c.setupServiceRequestCategory(patient, "RAD")
imagingStudyRad := c.setupImagingStudy(patient)
diagnosticReportIdRad := c.setupDiagnosticCategory(patient, "RAD")
observationIdRad := c.setupObservationCategory(patient, "RAD")
if serviceRequestIdLab != "" {
entryLab = append(entryLab, model.Reference{Reference: "ServiceRequest/" + serviceRequestIdLab})
}
if specimenIdLab != "" {
entryLab = append(entryLab, model.Reference{Reference: "Specimen/" + specimenIdLab})
}
if diagnosticReportIdLab != "" {
entryLab = append(entryLab, model.Reference{Reference: "DiagnosticReport/" + diagnosticReportIdLab})
}
if observationIdLab != "" {
entryLab = append(entryLab, model.Reference{Reference: "Observation/" + observationIdLab})
}
if serviceRequestIdRad != "" {
entryRad = append(entryRad, model.Reference{Reference: "ServiceRequest/" + serviceRequestIdRad})
}
if imagingStudyRad != "" {
entryRad = append(entryRad, model.Reference{Reference: "ImagingStudy/" + imagingStudyRad})
}
if diagnosticReportIdRad != "" {
entryRad = append(entryRad, model.Reference{Reference: "DiagnosticReport/" + diagnosticReportIdRad})
}
if observationIdRad != "" {
entryRad = append(entryRad, model.Reference{Reference: "Observation/" + observationIdRad})
}
if len(entryLab) != 0 && len(entryRad) != 0 {
req.Section = append(req.Section, model.SectionRequest{
Title: "Pemeriksaan Penunjang",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://terminology.kemkes.go.id",
Code: "TK000009",
Display: "Hasil Pemeriksaan Penunjang",
},
},
},
Text: &model.SectionText{
Status: "additional",
Div: req.PemeriksaanPenunjang,
},
Section: []model.SectionRequest{
{
Title: "Hasil Pemeriksaan Laboratorium",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "11502-2",
Display: "Laboratory report",
},
},
},
Entry: entryLab,
},
{
Title: "Hasil Pemeriksaan Radiologi",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "18782-3",
Display: "Radiology Study observation (narrative)",
},
},
},
Entry: entryRad,
},
},
})
} else if len(entryLab) != 0 && len(entryRad) == 0 {
req.Section = append(req.Section, model.SectionRequest{
Title: "Pemeriksaan Penunjang",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://terminology.kemkes.go.id",
Code: "TK000009",
Display: "Hasil Pemeriksaan Penunjang",
},
},
},
Text: &model.SectionText{
Status: "additional",
Div: req.PemeriksaanPenunjang,
},
Section: []model.SectionRequest{
{
Title: "Hasil Pemeriksaan Laboratorium",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "11502-2",
Display: "Laboratory report",
},
},
},
Entry: entryLab,
},
},
})
} else if len(entryLab) == 0 && len(entryRad) != 0 {
req.Section = append(req.Section, model.SectionRequest{
Title: "Pemeriksaan Penunjang",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://terminology.kemkes.go.id",
Code: "TK000009",
Display: "Hasil Pemeriksaan Penunjang",
},
},
},
Text: &model.SectionText{
Status: "additional",
Div: req.PemeriksaanPenunjang,
},
Section: []model.SectionRequest{
{
Title: "Hasil Pemeriksaan Radiologi",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "18782-3",
Display: "Radiology Study observation (narrative)",
},
},
},
Entry: entryRad,
},
},
})
}
var entryCondition []model.Reference
conditionId := c.setupCondition(patient)
if conditionId != "" {
entryCondition = append(entryCondition, model.Reference{Reference: "Condition/" + conditionId})
}
req.Section = append(req.Section, model.SectionRequest{
Title: "Diagnosa",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://terminology.kemkes.go.id",
Code: "TK000004",
Display: "Diagnosis",
},
},
},
Section: []model.SectionRequest{
{
Title: "Diagnosa Utama",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "78375-3",
Display: "Discharge diagnosis Narrative",
},
},
},
Entry: entryCondition,
},
},
})
var entryProcedure []model.Reference
procedureId := c.setupProcedure(patient)
if procedureId != "" {
entryProcedure = append(entryProcedure, model.Reference{Reference: "Procedure/" + procedureId})
}
req.Section = append(req.Section, model.SectionRequest{
Title: "Tindakan Operatif/Non Operatif Utama",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://terminology.kemkes.go.id",
Code: "TK000005",
Display: "Tindakan/Prosedur Medis",
},
},
},
Entry: entryProcedure,
})
var medication []model.Reference
medicationRequestId := c.setupMedicationRequest(patient)
if medicationRequestId != "" {
medication = append(medication, model.Reference{Reference: "MedicationRequest/" + medicationRequestId})
}
medicationDispenseId := c.setupMedicationDispense(patient)
if medicationDispenseId != "" {
medication = append(medication, model.Reference{Reference: "MedicationDispense/" + medicationDispenseId})
}
/* Farmasi section disabled for Now
req.Section = append(req.Section, model.SectionRequest{
Title: "Farmasi",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://terminology.kemkes.go.id",
Code: "TK000013",
Display: "Obat",
},
},
},
Section: []model.SectionRequest{
/*{
Title: "Obat Saat Kunjungan",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "42346-7",
Display: "Medications on admission (narrative)",
},
},
},
Entry: []model.Reference{
{Reference: "MedicationAdministration/"},
{Reference: "MedicationRequest/"},
{Reference: "MedicationDispense/"},
},
},*/
/* {
Title: "Obat Pulang",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "75311-1",
Display: "Discharge medications Narrative",
},
},
},
Entry: medication,
},
},
})
*/
req.Section = append(req.Section, model.SectionRequest{
Title: "Medikamentosa",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "18776-5",
Display: "Plan of treatment (narrative)",
},
},
},
Text: &model.SectionText{
Status: "additional",
Div: req.Medikamentosa,
},
})
req.Section = append(req.Section, model.SectionRequest{
Title: "Lanjutan Penatalaksanaan",
Code: model.CodeableConcept{
Coding: []model.Coding{
{
System: "http://loinc.org",
Code: "8653-8",
Display: "Hospital Discharge instructions",
},
},
},
Text: &model.SectionText{
Status: "additional",
Div: req.LanjutanPenatalaksanaan,
},
})
//remove field unused
req.Anamnesis = ""
req.PemeriksaanFisik = ""
req.PemeriksaanPenunjang = ""
req.Medikamentosa = ""
req.LanjutanPenatalaksanaan = ""
oauth := model.OauthRequest{
ClientId: c.akses.ClientId,
ClientSecret: c.akses.ClientSecret,
}
token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth)
if err != nil {
return nil, err
}
if token == nil {
return nil, errors.New(constant.ErrGenerateToken)
}
// b, err := json.MarshalIndent(req, "", " ")
// if err != nil {
// log.Println("error marshal:", err)
// } else {
// log.Println(string(b))
// }
url := c.akses.BaseUrl + "/Composition"
return httputil.DoRequest(httputil.RequestOption{
Method: "POST",
URL: url,
Body: req,
BearerToken: token.AccessToken,
})
}
// GetCompositionByID implements CompositionInterface.
func (c *CompositionRepository) GetCompositionByID(id string) (map[string]interface{}, error) {
panic("unimplemented")
}
// UpdateComposition implements CompositionInterface.
func (c *CompositionRepository) UpdateComposition(req model.CompositionRequest) (map[string]interface{}, error) {
req.ResourceType = "Composition"
patient, err := c.setupPatient(&req)
if err != nil {
return nil, err
}
err = c.setupEncounter(&req, patient)
if err != nil {
return nil, err
}
err = c.setupOrganization(&req)
if err != nil {
return nil, err
}
oauth := model.OauthRequest{
ClientId: c.akses.ClientId,
ClientSecret: c.akses.ClientSecret,
}
token, err := NewOauthRequestRepo(c.akses).GenerateToken(oauth)
if err != nil {
return nil, err
}
if token == nil {
return nil, errors.New(constant.ErrGenerateToken)
}
url := c.akses.BaseUrl + "/Composition/" + req.Id
return httputil.DoRequest(httputil.RequestOption{
Method: "PUT",
URL: url,
Body: req,
BearerToken: token.AccessToken,
})
}
func (c *CompositionRepository) setupPatient(req *model.CompositionRequest) (string, error) {
if req.Subject.Reference == "" {
return "", nil
}
patientInterface := NewPatientRepo(c.akses)
patient, err := patientInterface.HandleCheckPatient(req.Subject.Reference)
if err != nil {
return "", err
}
if patient != "" {
req.Subject.Reference = "Patient/" + patient
}
return patient, nil
}
// setupEncounter extracts encounter setup logic from CreateDiagnosisReport.
func (c *CompositionRepository) setupEncounter(req *model.CompositionRequest, patient string) error {
if patient == "" {
return nil
}
encounterInterface := NewEncounterRepo(c.akses)
encounterId, encounterExist, err := encounterInterface.HandleCheckEncounter(patient)
if err != nil {
return err
}
if encounterExist {
req.Encounter.Reference = "Encounter/" + encounterId
} else {
//Buat dulu encounter
}
return nil
}
func (c *CompositionRepository) setupOrganization(req *model.CompositionRequest) error {
req.Custodian.Reference = "Organization/" + os.Getenv("ORGANIZATION_ID")
return nil
}
func (c *CompositionRepository) setupPractitioner(req *model.CompositionRequest) error {
if len(req.Author) == 0 {
return nil
}
practicionerInterface := NewPracticionerRepo(c.akses)
ref, display, err := practicionerInterface.HandleCheckPartitioner(req.Author[0].Reference)
if err != nil {
return err
}
if ref != "" {
req.Author[0].Reference = "Practitioner/" + ref
req.Author[0].Display = display
}
return nil
}
func (c *CompositionRepository) setupAllergyIntollerance(patient string) string {
if patient == "" {
return ""
}
allergyInterface := NewAllergancyToleranRepo(c.akses)
allergyId, allergyExist, err := allergyInterface.HandleCheckAllergancyToleran(patient)
if err != nil {
return ""
}
if allergyExist {
return allergyId[0]
}
return ""
}
func (c *CompositionRepository) setupObservation(patient string) []model.Reference {
if patient == "" {
return nil
}
observationInterface := NewObservationRepo(c.akses)
observationId, observationExist, err := observationInterface.HandleCheckObservation(patient)
if err != nil {
return nil
}
if observationExist {
var references []model.Reference
for _, id := range observationId {
references = append(references, model.Reference{Reference: "Observation/" + id})
}
return references
}
return nil
}
func (c *CompositionRepository) setupClinicalImpression(patient string) string {
if patient == "" {
return ""
}
clinicalInterface := NewClinicalImpressionRepo(c.akses)
clinicalId, clinicalExist, err := clinicalInterface.HandleCheckClinicalImpression(patient)
if err != nil {
return ""
}
if clinicalExist {
return clinicalId[0]
}
return ""
}
func (c *CompositionRepository) setupGoal(patient string) string {
if patient == "" {
return ""
}
goalInterface := NewGoalRepo(c.akses)
goalId, goalExist, err := goalInterface.HandleCheckGoal(patient)
if err != nil {
return ""
}
if goalExist {
return goalId[0]
}
return ""
}
func (c *CompositionRepository) setupCarePlan(patient string) string {
if patient == "" {
return ""
}
careplanInterface := NewCarePlanRepo(c.akses)
careplanId, careplanExist, err := careplanInterface.HandleCheckCarePlan(patient)
if err != nil {
return ""
}
if careplanExist {
return careplanId[0]
}
return ""
}
func (c *CompositionRepository) setupServiceRequestCategory(patient string, category string) string {
if patient == "" {
return ""
}
if category == "LAB" {
category = "108252007"
} else if category == "RAD" {
category = "363679005"
}
serviceRequestInterface := NewServiceRequestRepository(c.akses)
serviceRequestId, serviceRequestExist, err := serviceRequestInterface.HandleCheckServiceRequestByCategory(patient, category)
if err != nil {
return ""
}
if serviceRequestExist {
return serviceRequestId[0]
}
return ""
}
func (c *CompositionRepository) setupSpecimenCategory(patient string) string {
if patient == "" {
return ""
}
specimenInterface := NewSpecimenRepository(c.akses)
specimenId, specimenExist, err := specimenInterface.HandleCheckSpecimen(patient)
if err != nil {
return ""
}
if specimenExist {
return specimenId[0]
}
return ""
}
func (c *CompositionRepository) setupImagingStudy(patient string) string {
if patient == "" {
return ""
}
imagingInterface := NewImagingStudyRepo(c.akses)
imagingId, imagingExist, err := imagingInterface.HandleCheckImagingStudy(patient)
if err != nil {
return ""
}
if imagingExist {
return imagingId[0]
}
return ""
}
func (c *CompositionRepository) setupObservationCategory(patient string, category string) string {
if patient == "" {
return ""
}
if category == "LAB" {
category = "laboratory"
} else if category == "RAD" {
category = "imaging"
}
observationInterface := NewObservationRepo(c.akses)
observationId, observationExist, err := observationInterface.HandleCheckObservationCategory(patient, category)
if err != nil {
return ""
}
if observationExist {
return observationId[0]
}
return ""
}
func (c *CompositionRepository) setupDiagnosticCategory(patient string, category string) string {
if patient == "" {
return ""
}
if category == "LAB" {
category = "LAB"
} else if category == "RAD" {
category = "RAD"
}
observationInterface := NewObservationRepo(c.akses)
observationId, observationExist, err := observationInterface.HandleCheckObservationCategory(patient, category)
if err != nil {
return ""
}
if observationExist {
return observationId[0]
}
return ""
}
func (c *CompositionRepository) setupCondition(patient string) string {
if patient == "" {
return ""
}
conditionInterface := NewConditionRepo(c.akses)
conditionId, conditionExist, err := conditionInterface.HandleCheckCondition(patient)
if err != nil {
return ""
}
if conditionExist {
return conditionId
}
return ""
}
func (c *CompositionRepository) setupProcedure(patient string) string {
if patient == "" {
return ""
}
procedureInterface := NewProcedureRepo(c.akses)
procedureId, procedureExist, err := procedureInterface.HandleCheckProcedure(patient)
if err != nil {
return ""
}
if procedureExist {
return procedureId[0]
}
return ""
}
func (c *CompositionRepository) setupMedicationRequest(patient string) string {
if patient == "" {
return ""
}
medicationRequestInterface := NewMedicationRequestRepo(c.akses)
medicationRequestId, medicationRequestExist, err := medicationRequestInterface.HandleCheckMedicationRequest(patient, "")
if err != nil {
return ""
}
if medicationRequestExist {
return medicationRequestId[0]
}
return ""
}
func (c *CompositionRepository) setupMedicationDispense(patient string) string {
if patient == "" {
return ""
}
medicationDispenseInterface := NewMedicationDispenseRepo(c.akses)
medicationDispenseId, medicationDispenseExist, err := medicationDispenseInterface.HandleCheckMedicationDispense(patient, "")
if err != nil {
return ""
}
if medicationDispenseExist {
return medicationDispenseId[0]
}
return ""
}
func NewCompositionRepo(akses *model.Akses) CompositionInterface {
return &CompositionRepository{
akses: akses,
}
}