package health import ( "errors" "sort" "time" "github.com/eternal-flame-AD/yoake/internal/db" "github.com/eternal-flame-AD/yoake/internal/echoerror" "github.com/eternal-flame-AD/yoake/internal/util" "github.com/google/uuid" ) func DBMedListGet(database db.DB) ([]Direction, error) { txn := database.NewTransaction(false) defer txn.Discard() var meds []Direction if err := db.GetJSON(txn, []byte("health_meds_list"), &meds); db.IsNotFound(err) { err = DBMedListSet(database, []Direction{}) return meds, err } else if err != nil { return nil, err } return meds, nil } func DBMedListSet(database db.DB, meds []Direction) error { txn := database.NewTransaction(true) defer txn.Discard() for i, med := range meds { _, meds[i].DirectionShorthand = med.ShortHand() } if err := db.SetJSON(txn, []byte("health_meds_list"), meds); err != nil { return err } return txn.Commit() } const dbMedComplianceLogPrefix = "health_meds_compliance_log_" func DBMedComplianceLogGet(database db.DB, dates util.DateRange) (ComplianceLogList, error) { txn := database.NewTransaction(false) defer txn.Discard() endIndex := dates.To.UTC().Format("2006-01") indexesToFetch := []string{endIndex} for indexNow := dates.From.UTC().AddDate(0, -1, 0); indexNow.Before(dates.To.AddDate(0, 1, 0)); indexNow = indexNow.AddDate(0, 1, 0) { indexesToFetch = append(indexesToFetch, indexNow.Format("2006-01")) } indexesToFetch = util.Unique(indexesToFetch) sort.Strings(indexesToFetch) var res ComplianceLogList for _, index := range indexesToFetch { var log []ComplianceLog if err := db.GetJSON(txn, []byte(dbMedComplianceLogPrefix+index), &log); db.IsNotFound(err) { continue } else if err != nil { return nil, err } res = append(res, log...) } sort.Sort(res) return res, nil } func DBMedComplianceLogAppend(database db.DB, pending ComplianceLogList) error { txn := database.NewTransaction(true) defer txn.Discard() for len(pending) > 0 { index := pending[0].Actual.Time.UTC().Format("2006-01") var origLogs ComplianceLogList if err := db.GetJSON(txn, []byte(dbMedComplianceLogPrefix+index), &origLogs); db.IsNotFound(err) { origLogs = []ComplianceLog{} } else if err != nil { return err } for i := len(pending) - 1; i >= 0; i-- { if pending[i].Actual.Time.UTC().Format("2006-01") != index { continue } origLogs = append(origLogs, pending[i]) pending = append(pending[:i], pending[i+1:]...) uuidMap := make(map[string]int) for j := len(origLogs) - 1; j >= 0; j-- { if _, ok := uuidMap[origLogs[j].UUID]; ok { origLogs = append(origLogs[:j], origLogs[j+1:]...) } else { uuidMap[origLogs[j].UUID] = j } } } sort.Sort(origLogs) if err := db.SetJSON(txn, []byte(dbMedComplianceLogPrefix+index), origLogs); err != nil { return err } } return txn.Commit() } func DBMedComplianceLogSetOne(database db.DB, dir Direction, log *ComplianceLog) error { index := log.Actual.Time.UTC().Format("2006-01") existingLogs, err := DBMedComplianceLogGet(database, util.DateRangeAround(log.Actual.Time, 1)) if err != nil { return err } txn := database.NewTransaction(true) defer txn.Discard() del := false if log.Actual.Dose == 0 { return echoerror.NewHttp(400, errors.New("dose cannot be zero")) } else if log.Actual.Dose < 0 { del = true } if log.UUID != "" { foundIdx := -1 for i, existingLog := range existingLogs { if existingLog.UUID == log.UUID { log.UpdatedAt = time.Now() foundIdx = i break } } if foundIdx < 0 { return echoerror.NewHttp(404, errors.New("log with specified UUID not found")) } origLog := existingLogs[foundIdx] log.CreatedAt = origLog.CreatedAt origLogIdx := origLog.Actual.Time.UTC().Format("2006-01") if origLogIdx == index { // update and return if del { existingLogs = append(existingLogs[:foundIdx], existingLogs[foundIdx+1:]...) } else { log.UpdatedAt = time.Now() existingLogs[foundIdx] = *log } sort.Sort(existingLogs) if err := db.SetJSON(txn, []byte(dbMedComplianceLogPrefix+index), existingLogs); err != nil { return err } return txn.Commit() } else { // delete from old index existingLogs = append(existingLogs[:foundIdx], existingLogs[foundIdx+1:]...) if err := db.SetJSON(txn, []byte(dbMedComplianceLogPrefix+origLogIdx), existingLogs); err != nil { return err } } } if del { return txn.Commit() } if log.UUID == "" { log.UUID = uuid.New().String() log.CreatedAt = time.Now() if log.Expected.Dose == 0 { nextDose := existingLogs.ProjectNextDose(dir) log.Expected.Time = nextDose.Expected.Time log.Expected.Dose = nextDose.Expected.Dose } } log.UpdatedAt = time.Now() var logs ComplianceLogList if err := db.GetJSON(txn, []byte(dbMedComplianceLogPrefix+index), &logs); db.IsNotFound(err) { logs = []ComplianceLog{*log} } else if err != nil { return err } else { logs = append(logs, *log) } logs.UpdateDoseOffset(dir) uuid := log.UUID sort.Sort(ComplianceLogList(logs)) if err := db.SetJSON(txn, []byte(dbMedComplianceLogPrefix+index), logs); err != nil { return err } for i, l := range logs { if l.UUID == uuid { *log = logs[i] } } return txn.Commit() }