462 lines
14 KiB
Go
462 lines
14 KiB
Go
package generatefile
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"mime"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
evs "simrs-vx/internal/domain/bpjs-entities/vclaim-sep"
|
|
evscl "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter"
|
|
ee "simrs-vx/internal/domain/main-entities/encounter"
|
|
egc "simrs-vx/internal/domain/main-entities/general-consent"
|
|
er "simrs-vx/internal/domain/main-entities/resume"
|
|
es "simrs-vx/internal/domain/main-entities/screening"
|
|
|
|
uvs "simrs-vx/internal/use-case/bpjs-use-case/vclaim-sep"
|
|
uvscl "simrs-vx/internal/use-case/bpjs-use-case/vclaim-sep-control-letter"
|
|
ue "simrs-vx/internal/use-case/main-use-case/encounter"
|
|
ugc "simrs-vx/internal/use-case/main-use-case/general-consent"
|
|
ur "simrs-vx/internal/use-case/main-use-case/resume"
|
|
us "simrs-vx/internal/use-case/main-use-case/screening"
|
|
|
|
ercl "simrs-vx/internal/domain/references/clinical"
|
|
erc "simrs-vx/internal/domain/references/common"
|
|
docscfg "simrs-vx/internal/infra/docs-cfg"
|
|
|
|
pc "simrs-vx/pkg/conv-helper"
|
|
pf "simrs-vx/pkg/file-helper"
|
|
pl "simrs-vx/pkg/logger"
|
|
pm "simrs-vx/pkg/minio-helper"
|
|
pp "simrs-vx/pkg/pdf-helper"
|
|
pu "simrs-vx/pkg/use-case-helper"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// generate temporary file, upload to minio, generate public url, delete temporary file
|
|
func generateFile(input GenerateDto, templateData any) (string, error) {
|
|
newPath, err := pf.PathToSaveFile(fmt.Sprintf("./temporary/%s", input.EntityType_Code))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
fPath := fmt.Sprintf("%s/%s-%s.%s", newPath, input.Type_Code, time.Now().Format("20060102150405"), input.FormatType)
|
|
|
|
templatePath := docscfg.O.GetPath() + string(input.TemplateName)
|
|
|
|
switch input.FormatType {
|
|
case erc.DFTCPDF:
|
|
if err := generatePDF(GeneratePDFdto{
|
|
TemplatePath: templatePath,
|
|
TemplateData: templateData,
|
|
PdfPath: fPath,
|
|
UseA5Lanscape: input.UseA5Lanscape,
|
|
}); err != nil {
|
|
return "", err
|
|
}
|
|
case erc.DFTCTXLSX:
|
|
// TODO: generate xlsx
|
|
case erc.DFTCTCSV:
|
|
// TODO: generate csv
|
|
default:
|
|
return "", errors.New("invalid format type")
|
|
}
|
|
|
|
bucketName := input.EntityType_Code
|
|
|
|
objectName := fmt.Sprintf("%s/%s-%d.%s", *pc.UintToString(input.Encounter_Id), input.Type_Code, time.Now().UnixNano(), input.FormatType)
|
|
pdfUpload := pm.UploadPathInput{
|
|
BucketName: string(bucketName),
|
|
Name: objectName,
|
|
Path: fPath,
|
|
ContentType: mime.TypeByExtension(filepath.Ext(fPath)),
|
|
}
|
|
|
|
// create bucket if not exist, create object in bucket
|
|
info, err := pm.I.FPutObject(pdfUpload)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// generate public url
|
|
urlPub := pm.I.GenerateUrl(info.Bucket, info.Key)
|
|
if err := pf.DeleteFolder(fPath); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return urlPub, nil
|
|
}
|
|
|
|
func generatePDF(input GeneratePDFdto) error {
|
|
// parse template data into html template
|
|
r := pp.NewRequestPdf("")
|
|
if err := r.ParseTemplate(input.TemplatePath, input.TemplateData); err == nil {
|
|
_, err := r.GenerateByCommand(input.PdfPath, input.TemplatePath, input.UseA5Lanscape)
|
|
if err != nil {
|
|
return errors.New("generate pdf by command error : " + err.Error())
|
|
}
|
|
} else {
|
|
return errors.New("parse template error : " + err.Error())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func removeFile(bucket, fileUrl string) error {
|
|
fPath := pu.GetLastTwoPathSegments(fileUrl)
|
|
err := pm.I.RemoveObject(bucket, fPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// generate general consent
|
|
func generateGC(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, error) {
|
|
// get value from general consent by ref_id
|
|
gc, err := ugc.ReadDetailData(egc.ReadDetailDto{Id: uint(*pc.StringToUint64(*input.Ref_Id))}, &event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if gc.FileUrl != nil {
|
|
if err := removeFile(string(input.EntityType_Code), *gc.FileUrl); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// map template data
|
|
templateData := GeneralConsentPDF{}
|
|
if gc.Value != nil {
|
|
err := json.Unmarshal([]byte(*gc.Value), &templateData)
|
|
if err != nil {
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-unmarshal-fail",
|
|
Detail: err.Error(),
|
|
Raw: err,
|
|
}
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return nil, errors.New("no value in this general consent")
|
|
}
|
|
|
|
input.FormatType = erc.DFTCPDF
|
|
input.TemplateName = TDNGC
|
|
input.Encounter_Id = gc.Encounter_Id
|
|
templateData.Date = pu.FormatIndonesianDate(gc.CreatedAt)
|
|
|
|
// generate file
|
|
urlPub, err := generateFile(input, templateData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
gc.FileUrl = &urlPub
|
|
if err := tx.Save(&gc).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response := ResponseDto{
|
|
FileUrl: urlPub,
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
func generateCL(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, error) {
|
|
// get value from control letter by ref_id
|
|
cl, err := uvscl.ReadDetailData(evscl.ReadDetailDto{Number: input.Ref_Id}, &event)
|
|
if err != nil {
|
|
if !pu.IsDataNotFoundError(err) {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if cl != nil && cl.FileUrl != nil {
|
|
if err := removeFile(string(input.EntityType_Code), *cl.FileUrl); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// map template data
|
|
clData := evscl.ResponseForPDF{}
|
|
if cl.Value != nil {
|
|
err := json.Unmarshal([]byte(*cl.Value), &clData)
|
|
if err != nil {
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-unmarshal-fail",
|
|
Detail: err.Error(),
|
|
Raw: err,
|
|
}
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return nil, errors.New("there is no value to be used")
|
|
}
|
|
|
|
// get encounter id by vclaim sep number
|
|
vs, err := uvs.ReadDetailData(evs.ReadDetailDto{Number: cl.VclaimSep_Number}, &event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
input.FormatType = erc.DFTCPDF
|
|
input.TemplateName = TDNCL
|
|
input.Encounter_Id = vs.Encounter_Id
|
|
input.UseA5Lanscape = true
|
|
|
|
templateData := generateTemplateData(clData)
|
|
// generate file
|
|
urlPub, err := generateFile(input, templateData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cl.FileUrl = &urlPub
|
|
if err := tx.Save(&cl).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response := ResponseDto{
|
|
FileUrl: urlPub,
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
func generateResume(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, error) {
|
|
// get value from resume by ref_id
|
|
includes := "Doctor.Employee.Person"
|
|
r, err := ur.ReadDetailData(er.ReadDetailDto{Id: uint(*pc.StringToUint64(*input.Ref_Id)), Includes: includes}, &event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !r.IsValidated() {
|
|
return nil, errors.New("resume is not validated")
|
|
}
|
|
|
|
if r.FileUrl != nil {
|
|
if err := removeFile(string(input.EntityType_Code), *r.FileUrl); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
templateData, err := generateResumeTemplateData(*r, event, tx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
input.FormatType = erc.DFTCPDF
|
|
input.TemplateName = TDNR
|
|
input.Encounter_Id = r.Encounter_Id
|
|
|
|
// generate file
|
|
urlPub, err := generateFile(input, templateData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r.FileUrl = &urlPub
|
|
if err := tx.Save(&r).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response := ResponseDto{
|
|
FileUrl: urlPub,
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
func generateResumeTemplateData(resume er.Resume, event pl.Event, tx *gorm.DB) (*ResumePDF, error) {
|
|
// get encounter
|
|
includes := "Patient,Patient.Person,Patient.Person.BirthRegency,Patient.Person.Contacts"
|
|
encounter, err := ue.ReadDetailData(ee.ReadDetailDto{Id: *resume.Encounter_Id, Includes: includes}, &event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// map template data
|
|
rData := er.ValueDto{}
|
|
if resume.Value != nil {
|
|
err := json.Unmarshal([]byte(*resume.Value), &rData)
|
|
if err != nil {
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-unmarshal-fail",
|
|
Detail: err.Error(),
|
|
Raw: err,
|
|
}
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return nil, errors.New("there is no data to be used")
|
|
}
|
|
templateData := ResumePDF{}
|
|
templateData.MedicalRecord = *encounter.Patient.Number
|
|
templateData.NIK = *encounter.Patient.Person.ResidentIdentityNumber
|
|
templateData.Name = encounter.Patient.Person.Name
|
|
templateData.BirthPlaceDate = pu.FormatPlaceAndDate(encounter.Patient.Person.BirthRegency.Name, *encounter.Patient.Person.BirthDate)
|
|
templateData.Gender = encounter.Patient.Person.GenderString()
|
|
templateData.Phone = encounter.Patient.Person.GetPhoneNumber()
|
|
// templateData.Class = get from vclaim sep eg. III
|
|
// templateData.Unit = get from vclaim sep eg. R.KERINCI - KLS 3
|
|
templateData.Doctor_Name = resume.Doctor.Employee.Person.FullName()
|
|
templateData.StartedAt = *pc.TimeToStringWithFormat(encounter.StartedAt, "")
|
|
templateData.FinishedAt = *pc.TimeToStringWithFormat(encounter.FinishedAt, "")
|
|
templateData.DiagnosisIn = rData.Assessment.DiagnosisIn
|
|
templateData.Diagnosis = rData.Diagnosis.SecondaryDiagnosis
|
|
templateData.MainComplaint = rData.Assessment.MainComplaint
|
|
templateData.MedicalHistory = rData.Assessment.MedicalHistory
|
|
templateData.PhysicalExamination = rData.Assessment.PhysicalExamination
|
|
templateData.SupportingExamination = rData.Supporting.Notes
|
|
templateData.MedicalActions = rData.Action.AdditionalActions
|
|
templateData.MedicalAction = rData.Action.MedicalActions
|
|
templateData.Consultations = rData.Consultation.Consultations
|
|
templateData.Allergy = rData.Pharmacy.AllergySpecialConditions
|
|
templateData.ConsciousnessLevel = rData.Discharge.ConsciousnessLevel
|
|
templateData.BloodPressure = fmt.Sprintf("%.0f/%.0f", rData.Discharge.BloodPressureSystolic, rData.Discharge.BloodPressureDiastolic)
|
|
templateData.BodyTemperature = rData.Discharge.BodyTemperature
|
|
templateData.RespirationRate = rData.Discharge.RespirationRate
|
|
templateData.HeartRate = rData.Discharge.HeartRate
|
|
templateData.PainScale = rData.Discharge.PainScale
|
|
templateData.ConditionOnDischarge = rData.Management.ConditionOnDischarge
|
|
templateData.DischargeMethod = rData.Management.DischargeMethod
|
|
templateData.Medications = rData.Medication.Medications
|
|
templateData.Date = pu.GetTimeNow().Format("02-01-2006")
|
|
|
|
return &templateData, nil
|
|
}
|
|
|
|
func generateScreeningTemplateData(screenings []es.Screening, event pl.Event, tx *gorm.DB) (*ScreeningPDF, error) {
|
|
// get encounter
|
|
includes := "Patient,Patient.Person"
|
|
encounter, err := ue.ReadDetailData(ee.ReadDetailDto{Id: *screenings[0].Encounter_Id, Includes: includes}, &event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch screenings[0].Type {
|
|
case ercl.SFTCA:
|
|
// map template data
|
|
sData := es.FormA{}
|
|
if screenings[0].Value != nil {
|
|
err := json.Unmarshal([]byte(*screenings[0].Value), &sData)
|
|
if err != nil {
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-unmarshal-fail",
|
|
Detail: err.Error(),
|
|
Raw: err,
|
|
}
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return nil, errors.New("there is no data to be used")
|
|
}
|
|
|
|
templateData := ScreeningPDF{}
|
|
templateData.IssuedDate = pu.GetTimeNow().Format("2006-01-02 15:04:05")
|
|
templateData.MedicalRecord = *encounter.Patient.Number
|
|
templateData.Name = encounter.Patient.Person.FullName()
|
|
templateData.BirthDate = encounter.Patient.Person.BirthDate.Format("2006-01-02 15:04:05")
|
|
templateData.Employee_Name = screenings[0].Employee.Person.FullName()
|
|
templateData.EarlyMedic = sData.Screening.SelectedScreeningLabels()
|
|
templateData.Assessment = sData.AssessmentDetail
|
|
templateData.ProblemIdentification = sData.ProblemIdentification.SelectedProblemLabels()
|
|
templateData.Planning = sData.PlanningDetail
|
|
templateData.Date = screenings[0].CreatedAt.Format("2006-01-02 15:04:05")
|
|
|
|
return &templateData, nil
|
|
case ercl.SFTCB:
|
|
// map template data
|
|
templateData := ScreeningPDF{}
|
|
templateData.IssuedDate = pu.GetTimeNow().Format("2006-01-02 15:04:05")
|
|
templateData.MedicalRecord = *encounter.Patient.Number
|
|
templateData.Name = encounter.Patient.Person.FullName()
|
|
templateData.BirthDate = encounter.Patient.Person.BirthDate.Format("2006-01-02 15:04:05")
|
|
for k, v := range screenings {
|
|
templateData.FormB = append(templateData.FormB, ScreeningFormBPDF{
|
|
Number: k + 1,
|
|
Date: v.CreatedAt.Format("2006-01-02 15:04:05"),
|
|
Employee_Name: v.Employee.Person.FullName(),
|
|
Value: *v.Value,
|
|
})
|
|
}
|
|
|
|
return &templateData, nil
|
|
}
|
|
return nil, errors.New("invalid screening type")
|
|
}
|
|
|
|
func generateScreening(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, error) {
|
|
// get value from resume by ref_id
|
|
includes := "Employee.Person"
|
|
s, err := us.ReadDetailData(es.ReadDetailDto{Id: uint(*pc.StringToUint64(*input.Ref_Id)), Includes: includes}, &event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var screenings []es.Screening
|
|
var templateData *ScreeningPDF
|
|
if s.IsFormA() {
|
|
if s.FileUrl != nil {
|
|
if err := removeFile(string(input.EntityType_Code), *s.FileUrl); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
screenings = append(screenings, *s)
|
|
input.FormatType = erc.DFTCPDF
|
|
input.TemplateName = TDNSA
|
|
input.Encounter_Id = s.Encounter_Id
|
|
templateData, err = generateScreeningTemplateData(screenings, event, tx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
includes := "Employee.Person"
|
|
sort := "CreatedAt:ASC"
|
|
ss, _, err := us.ReadListData(es.ReadListDto{FilterDto: es.FilterDto{Encounter_Id: s.Encounter_Id, Type: ercl.SFTCB}, Includes: includes, Sort: sort}, &event, tx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
screenings = append(screenings, ss...)
|
|
input.FormatType = erc.DFTCPDF
|
|
input.TemplateName = TDNSB
|
|
input.Encounter_Id = s.Encounter_Id
|
|
templateData, err = generateScreeningTemplateData(screenings, event, tx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// generate file
|
|
urlPub, err := generateFile(input, templateData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(screenings) > 1 {
|
|
for i := range screenings {
|
|
screenings[i].FileUrl = &urlPub
|
|
}
|
|
if err := tx.Save(&screenings).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
s.FileUrl = &urlPub
|
|
if err := tx.Save(&s).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
response := ResponseDto{
|
|
FileUrl: urlPub,
|
|
}
|
|
|
|
return &response, nil
|
|
}
|