From 9722527c4c6cc4cbfc2f64cb3cdec740f527f989 Mon Sep 17 00:00:00 2001 From: Samiul Hoque Date: Wed, 27 Feb 2019 14:31:54 +0600 Subject: [PATCH] Add medications rule support --- contents/guideline_hearts.json | 31 ++++++++++++++++++++++++++++ pkg/algorithms/hearts/hearts.go | 8 ++++---- pkg/engine/blood_pressure.go | 33 +++++++++++++++++++++++------- pkg/engine/cholesterol.go | 19 +++++++++++------ pkg/engine/cvd.go | 24 ++++++++++++++-------- pkg/engine/diabetes.go | 18 ++++++++++++----- pkg/tools/params.go | 36 ++++++++++++++++++--------------- sample-request.json | 9 +++++---- 8 files changed, 128 insertions(+), 50 deletions(-) diff --git a/contents/guideline_hearts.json b/contents/guideline_hearts.json index fc9049f..5a2dc05 100644 --- a/contents/guideline_hearts.json +++ b/contents/guideline_hearts.json @@ -714,6 +714,37 @@ ], "code": "BP-HTN-DM" }, + { + "category": "hypertension, on medication", + "definition": "high blood pressure, on medication", + "conditions": [{ + "medications": { + "anti-hypertensive": true + }, + "diabetes": false, + "age": { + "from": 41 + }, + "sbp": { + "from": 141, + "unit": "mmHg" + }, + "target": "120/80" + }, + { + "medications": { + "anti-hypertensive": true + }, + "diabetes": false, + "dbp": { + "from": 91, + "unit": "mmHg" + }, + "target": "120/80" + } + ], + "code": "BP-HTN-ON-MEDICATION" + }, { "category": "hypertension", "definition": "high blood pressure", diff --git a/pkg/algorithms/hearts/hearts.go b/pkg/algorithms/hearts/hearts.go index 61e7abf..465ce8b 100644 --- a/pkg/algorithms/hearts/hearts.go +++ b/pkg/algorithms/hearts/hearts.go @@ -378,7 +378,7 @@ func (d *Data) get(ctx context.Context) error { } // Diabetes - diabetes, err := engineGuide.Body.Diabetes.Process(p.Diabetes, bslOrA1c, bslOrA1cType, bslOrA1cUnit) + diabetes, err := engineGuide.Body.Diabetes.Process(p.Diabetes, bslOrA1c, bslOrA1cType, bslOrA1cUnit, p.Medications) if err != nil { errs = append(errs, err.Error()) } else { @@ -401,7 +401,7 @@ func (d *Data) get(ctx context.Context) error { if diabetes.Value == "diabetes" { diab = true } - bp, err := engineGuide.Body.BloodPressure.Process(diab, p.Sbp, p.Dbp, p.Age) + bp, err := engineGuide.Body.BloodPressure.Process(diab, p.Sbp, p.Dbp, p.Age, p.Medications) if err != nil { errs = append(errs, err.Error()) } else { @@ -421,7 +421,7 @@ func (d *Data) get(ctx context.Context) error { // CVD cvdScore := "" - cvd, err := engineGuide.Body.CVD.Guidelines.Process(ctx, p.AMI, p.Cvd, p.Pvd, p.Ckd, p.Age, *engineGuide.Body.CVD.PreProcessing) + cvd, err := engineGuide.Body.CVD.Guidelines.Process(ctx, p.AMI, p.Cvd, p.Pvd, p.Ckd, p.Age, *engineGuide.Body.CVD.PreProcessing, p.Medications) if err == nil { cvdScore = cvd.Value res, followupActions = GetResults(cvd, *engineContent.Body.Contents, followupActions) @@ -456,7 +456,7 @@ func (d *Data) get(ctx context.Context) error { cvdForChol = 10.0 } // fmt.Println("CVD for Chol: ", cvdForChol) - chol, err := engineGuide.Body.Cholesterol.TotalCholesterol.Process(cvdForChol, p.Age, p.TChol, p.CholUnit, "total cholesterol") + chol, err := engineGuide.Body.Cholesterol.TotalCholesterol.Process(cvdForChol, p.Age, p.TChol, p.CholUnit, "total cholesterol", p.Medications) if err != nil { errs = append(errs, err.Error()) } else { diff --git a/pkg/engine/blood_pressure.go b/pkg/engine/blood_pressure.go index 2744b68..355b85c 100644 --- a/pkg/engine/blood_pressure.go +++ b/pkg/engine/blood_pressure.go @@ -5,13 +5,25 @@ import ( "math" ) +// MedicationConditions object +type MedicationConditions struct { + Antihypertensive *bool `json:"anti-hypertensive"` + OralHypoglycaemic *bool `json:"oral-hypoglycaemic"` + Insulin *bool `json:"insulin"` + LipidLowering *bool `json:"lipid-lowering"` + Antiplatelet *bool `json:"anti-platelet"` + AntiCoagulant *bool `json:"anti-coagulant"` + Bronchodilator *bool `json:"bronchodilator"` +} + // BloodPressureCondition object type BloodPressureCondition struct { - Diabetes *bool `json:"diabetes"` - SBP *RangeInt `json:"sbp"` - DBP *RangeInt `json:"dbp"` - Age *RangeFloat `json:"age"` - Target *string `json:"target"` + Medications *MedicationConditions `json:"medications"` + Diabetes *bool `json:"diabetes"` + SBP *RangeInt `json:"sbp"` + DBP *RangeInt `json:"dbp"` + Age *RangeFloat `json:"age"` + Target *string `json:"target"` } // BloodPressureConditions slice @@ -29,7 +41,7 @@ type BloodPressureGuideline struct { type BloodPressureGuidelines []BloodPressureGuideline // Process function -func (b *BloodPressureGuidelines) Process(diabetes bool, sbp, dbp int, age float64) (Response, error) { +func (b *BloodPressureGuidelines) Process(diabetes bool, sbp, dbp int, age float64, medications map[string]bool) (Response, error) { code := "" value := fmt.Sprintf("%d/%d", sbp, dbp) target := "" @@ -72,12 +84,19 @@ func (b *BloodPressureGuidelines) Process(diabetes bool, sbp, dbp int, age float } } + conditionMedication := true + if c.Medications != nil { + if c.Medications.Antihypertensive != nil && *c.Medications.Antihypertensive != medications["anti-hypertensive"] { + conditionMedication = false + } + } + conditionDiabetes := true if c.Diabetes != nil && *c.Diabetes != diabetes { conditionDiabetes = false } - if conditionDiabetes && (age >= ageFrom && age <= ageTo) && sbpFrom <= sbp && sbpTo >= sbp && dbpFrom <= dbp && dbpTo >= dbp { + if conditionDiabetes && conditionMedication && (age >= ageFrom && age <= ageTo) && sbpFrom <= sbp && sbpTo >= sbp && dbpFrom <= dbp && dbpTo >= dbp { code = *g.Code target = *c.Target break diff --git a/pkg/engine/cholesterol.go b/pkg/engine/cholesterol.go index f368590..0a74c6d 100644 --- a/pkg/engine/cholesterol.go +++ b/pkg/engine/cholesterol.go @@ -17,10 +17,11 @@ type CholesterolGuidelinesFull struct { // CholesterolCondition object type CholesterolCondition struct { - Age *RangeFloat `json:"age"` - CVD *RangeFloat `json:"cvd"` - Range *RangeFloat `json:"range"` - Target *string `json:"target"` + Medications *MedicationConditions `json:"medications"` + Age *RangeFloat `json:"age"` + CVD *RangeFloat `json:"cvd"` + Range *RangeFloat `json:"range"` + Target *string `json:"target"` } // CholesterolConditions slice @@ -38,7 +39,7 @@ type CholesterolGuideline struct { type CholesterolGuidelines []CholesterolGuideline // Process function -func (b *CholesterolGuidelines) Process(cvd, age, chol float64, cholUnit, cholType string) (Response, error) { +func (b *CholesterolGuidelines) Process(cvd, age, chol float64, cholUnit, cholType string, medications map[string]bool) (Response, error) { cholesterol := tools.CalculateMMOLValue(chol, cholUnit) code := "" @@ -87,9 +88,15 @@ func (b *CholesterolGuidelines) Process(cvd, age, chol float64, cholUnit, cholTy } } + conditionMedication := true + if c.Medications != nil { + if c.Medications.LipidLowering != nil && *c.Medications.LipidLowering != medications["lipid-lowering"] { + conditionMedication = false + } + } // fmt.Println("CHOL =>", cholFrom, cholTo, cholUnit) - if (age >= ageFrom && age <= ageTo) && (cvd >= cvdFrom && cvd <= cvdTo) && (cholesterol >= cholFrom && cholesterol <= cholTo) { + if conditionMedication && (age >= ageFrom && age <= ageTo) && (cvd >= cvdFrom && cvd <= cvdTo) && (cholesterol >= cholFrom && cholesterol <= cholTo) { code = *g.Code target = *c.Target break diff --git a/pkg/engine/cvd.go b/pkg/engine/cvd.go index 9ea29a6..369a7e3 100644 --- a/pkg/engine/cvd.go +++ b/pkg/engine/cvd.go @@ -96,11 +96,12 @@ func (p *PreProcessingGuidelines) PreProcess(ami, hxCVD, hxPVD, hxCKD bool, age // CVDCondition object type CVDCondition struct { - ExistingCVD *bool `json:"existing_cvd"` - HighRiskCondition *bool `json:"high_risk_conditions"` - AgeCheckForCVD *bool `json:"age_check_for_cvd"` - Range *RangeFloat `json:"range"` - Target *string `json:"target"` + Medications *MedicationConditions `json:"medications"` + ExistingCVD *bool `json:"existing_cvd"` + HighRiskCondition *bool `json:"high_risk_conditions"` + AgeCheckForCVD *bool `json:"age_check_for_cvd"` + Range *RangeFloat `json:"range"` + Target *string `json:"target"` } // CVDConditions slice @@ -118,7 +119,7 @@ type CVDGuideline struct { type CVDGuidelines []CVDGuideline // Process function -func (b *CVDGuidelines) Process(ctx context.Context, ami, hxCVD, hxPVD, hxCKD bool, age float64, preProcessing PreProcessing) (Response, error) { +func (b *CVDGuidelines) Process(ctx context.Context, ami, hxCVD, hxPVD, hxCKD bool, age float64, preProcessing PreProcessing, medications map[string]bool) (Response, error) { code := "" value := "" target := "" @@ -178,18 +179,25 @@ func (b *CVDGuidelines) Process(ctx context.Context, ami, hxCVD, hxPVD, hxCKD bo conditionHighRisk = false } + conditionMedication := true + if c.Medications != nil { + if (c.Medications.Antiplatelet != nil && *c.Medications.Antiplatelet != medications["anti-platelet"]) || (c.Medications.AntiCoagulant != nil && *c.Medications.AntiCoagulant != medications["anti-coagulant"]) { + conditionMedication = false + } + } + // res2B, _ := json.Marshal(c) // fmt.Println(string(res2B)) // fmt.Printf("%+v\n", conditionAge) // fmt.Printf("%+v\n", conditionExistingCVD) // fmt.Printf("%+v\n", conditionHighRisk) - if conditionAge && conditionExistingCVD && conditionHighRisk && (riskScore >= rangeFrom && riskScore <= rangeTo) { + if conditionMedication && conditionAge && conditionExistingCVD && conditionHighRisk && (riskScore >= rangeFrom && riskScore <= rangeTo) { code = *g.Code if code != "CVD-AGE-FALSE" { value = fmt.Sprintf("%s%%", riskRange) } else { - value = "1" + value = "0" } target = *c.Target break diff --git a/pkg/engine/diabetes.go b/pkg/engine/diabetes.go index 849d9b0..503ff6a 100644 --- a/pkg/engine/diabetes.go +++ b/pkg/engine/diabetes.go @@ -18,9 +18,10 @@ type BloodSugarCondition struct { // DiabetesCondition object type DiabetesCondition struct { - HxDiabetes *bool `json:"hx_diabetes"` - BloodSugar *BloodSugarCondition `json:"blood_sugar"` - Target *string `json:"target"` + Medications *MedicationConditions `json:"medications"` + HxDiabetes *bool `json:"hx_diabetes"` + BloodSugar *BloodSugarCondition `json:"blood_sugar"` + Target *string `json:"target"` } // DiabetesConditions slice @@ -38,7 +39,7 @@ type DiabetesGuideline struct { type DiabetesGuidelines []DiabetesGuideline // Process function -func (b *DiabetesGuidelines) Process(hxDiabetes bool, bsFromInput float64, bsType, unit string) (Response, error) { +func (b *DiabetesGuidelines) Process(hxDiabetes bool, bsFromInput float64, bsType, unit string, medications map[string]bool) (Response, error) { code := "" value := "" target := "" @@ -75,12 +76,19 @@ func (b *DiabetesGuidelines) Process(hxDiabetes bool, bsFromInput float64, bsTyp givenBsType = *c.BloodSugar.Type } + conditionMedication := true + if c.Medications != nil { + if (c.Medications.OralHypoglycaemic != nil && *c.Medications.OralHypoglycaemic != medications["oral-hypoglycaemic"]) || (c.Medications.Insulin != nil && *c.Medications.Insulin != medications["insulin"]) { + conditionMedication = false + } + } + conditionHxDiabetes := true if c.HxDiabetes != nil && *c.HxDiabetes != hxDiabetes { conditionHxDiabetes = false } - if conditionHxDiabetes && bsFrom <= from && bsTo >= from && strings.ToLower(bsType) == strings.ToLower(givenBsType) { + if conditionHxDiabetes && conditionMedication && bsFrom <= from && bsTo >= from && strings.ToLower(bsType) == strings.ToLower(givenBsType) { code = *g.Code target = *c.Target value = fmt.Sprintf("%.1f%s", bsFromInput, unit) diff --git a/pkg/tools/params.go b/pkg/tools/params.go index c5de28a..3db504d 100644 --- a/pkg/tools/params.go +++ b/pkg/tools/params.go @@ -59,12 +59,7 @@ type FamilyHistory struct { } // Medications object -type Medications struct { - Antihypertensives bool - Statin bool - Antiplatelet bool - Bronchodilator bool -} +type Medications map[string]bool // Measurements object type Measurements struct { @@ -452,22 +447,31 @@ func getInputs(data []byte) (Params, error) { }, "params", "components", "family_history") // Medications + out.Medications = make(map[string]bool) jp.ArrayEach(data, func(value []byte, dataType jp.ValueType, offset int, err error) { category := "" if stringValue, err = jp.GetString(value, "category"); err == nil { category = stringValue } - switch category { - case "anti-hypertensive": - out.Antihypertensives = true - case "statin": - out.Statin = true - case "antiplatelet": - out.Antiplatelet = true - case "bronchodilator": - out.Bronchodilator = true - } + out.Medications[category] = true + + // switch category { + // case "anti-hypertensive": + // out.Antihypertensives = true + // case "oral-hypoglycaemic": + // out.OralHypoglycaemic = true + // case "insulin": + // out.Insulin = true + // case "lipid-lowering": + // out.LipidLowering = true + // case "anti-platelet": + // out.Antiplatelet = true + // case "anti-coagulant": + // out.AntiCoagulant = true + // case "bronchodilator": + // out.Bronchodilator = true + // } }, "params", "components", "medications") // Medical history diff --git a/sample-request.json b/sample-request.json index a2dfc7f..a197e56 100644 --- a/sample-request.json +++ b/sample-request.json @@ -68,7 +68,7 @@ "effectiveDate": "timestamp", "name": "blood_pressure", "category": "vital-signs", - "value": "110/85", + "value": "150/95", "units": "mmHg", "arm": "right" }, @@ -76,7 +76,7 @@ "effectiveDate": "timestamp", "name": "blood_pressure", "category": "vital-signs", - "value": "125/80", + "value": "145/90", "units": "mmHg", "arm": "right" }, @@ -84,7 +84,7 @@ "effectiveDate": "timestamp", "name": "blood_pressure", "category": "vital-signs", - "value": "115/78", + "value": "148/88", "units": "mmHg", "arm": "right" }, @@ -210,7 +210,8 @@ "medications": [ { "generic": "rampiril", - "category": "anti-hypertensive" + "category": "anti-hypertensive", + "class": "" } ], "family_history": [