func (r *RoutesSuite) TestPieRoute(c *C) { // Insert the pie in the database patientURL := "http://testurl.org" pie := plugin.NewPie(patientURL) r.Database.C("pies").Insert(pie) // Now get the pie from the server pieURL := fmt.Sprintf("%s/pies/%s", r.Server.URL, pie.Id.Hex()) resp, err := http.Get(pieURL) util.CheckErr(err) c.Assert(resp.StatusCode, Equals, http.StatusOK) // Does the pie data contain the patient URL? defer resp.Body.Close() buf := new(bytes.Buffer) buf.ReadFrom(resp.Body) responseBody := buf.String() c.Assert(strings.Contains(responseBody, patientURL), Equals, true) }
// Calculate takes a stream of events and returns a slice of corresponding risk calculation results func (c *SimplePlugin) Calculate(es *plugin.EventStream, fhirEndpointURL string) ([]plugin.RiskServiceCalculationResult, error) { var results []plugin.RiskServiceCalculationResult // Keep a map of active conditions and medications -- so we don't double-count duplicates in the record. cMap := make(map[string]int) mMap := make(map[string]int) // Create the initial pie pie := plugin.NewPie(fhirEndpointURL + "/Patient/" + es.Patient.Id) pie.Slices = c.Config().DefaultPieSlices // Now go through the event stream, updating the pie for _, event := range es.Events { // NOTE: guard against future dates (for example, our patient generator can create future events) if event.Date.Local().After(time.Now()) { continue } var isFactor bool pie = pie.Clone(true) switch r := event.Value.(type) { case *models.Condition: if r.Code == nil || len(r.Code.Coding) == 0 { continue } isFactor = true key := r.Code.Coding[0].System + "|" + r.Code.Coding[0].Code count := cMap[key] if !event.End { cMap[key] = count + 1 } else if count > 0 { cMap[key] = count - 1 } pie.UpdateSliceValue("Conditions", calculateCount(cMap)) case *models.MedicationStatement: if r.MedicationCodeableConcept == nil || len(r.MedicationCodeableConcept.Coding) == 0 { continue } isFactor = true key := r.MedicationCodeableConcept.Coding[0].System + "|" + r.MedicationCodeableConcept.Coding[0].Code count := mMap[key] if !event.End { mMap[key] = count + 1 } else if count > 0 { mMap[key] = count - 1 } pie.UpdateSliceValue("Medications", calculateCount(mMap)) } if isFactor { score := pie.TotalValues() results = append(results, plugin.RiskServiceCalculationResult{ AsOf: event.Date, Score: &score, ProbabilityDecimal: nil, Pie: pie, }) } } // If there are no results, provide a 0 score for the current time if len(results) == 0 { zero := 0 results = append(results, plugin.RiskServiceCalculationResult{ AsOf: time.Now(), Score: &zero, ProbabilityDecimal: nil, Pie: pie, }) } return results, nil }
// Calculate takes a stream of events and returns a slice of corresponding risk calculation results func (c *CHA2DS2VAScPlugin) Calculate(es *plugin.EventStream, fhirEndpointURL string) ([]plugin.RiskServiceCalculationResult, error) { var results []plugin.RiskServiceCalculationResult // First make sure there is AFIB in the history, since this score is only valid for patients with AFIB var hasAFib bool for i := 0; !hasAFib && i < len(es.Events); i++ { if es.Events[i].Type == "Condition" && !es.Events[i].End { if cond, ok := es.Events[i].Value.(*models.Condition); ok { hasAFib = fuzzyFindCondition("427.31", "http://hl7.org/fhir/sid/icd-9", cond) } } } if !hasAFib { return nil, plugin.NewNotApplicableError("CHA2DS2-VASc is only applicable to patients with Atrial Fibrillation") } // Create the initial pie based on gender pie := plugin.NewPie(fhirEndpointURL + "/Patient/" + es.Patient.Id) pie.Slices = c.Config().DefaultPieSlices if es.Patient.Gender == "female" { pie.UpdateSliceValue("Gender", 1) } else if es.Patient.Gender == "male" { pie.UpdateSliceValue("Gender", 0) } // Now go through the event stream, updating the pie var hasAfib bool for _, event := range es.Events { // NOTE: We are not paying attention to end times -- if it's in the patient history, we count it. // Also guard against future dates (for example, our patient generator can create future events) if event.End || event.Date.Local().After(time.Now()) { continue } var isFactor bool pie = pie.Clone(true) switch event.Type { case "Condition": r := event.Value.(*models.Condition) if fuzzyFindCondition("427.31", "http://hl7.org/fhir/sid/icd-9", r) { // Found atrial fibrillation, so all events from here on should produce scores hasAfib = true isFactor = true } else if fuzzyFindCondition("428", "http://hl7.org/fhir/sid/icd-9", r) { pie.UpdateSliceValue("Congestive Heart Failure", 1) isFactor = true } else if fuzzyFindCondition("401", "http://hl7.org/fhir/sid/icd-9", r) { pie.UpdateSliceValue("Hypertension", 1) isFactor = true } else if fuzzyFindCondition("250", "http://hl7.org/fhir/sid/icd-9", r) { pie.UpdateSliceValue("Diabetes", 1) isFactor = true } else if fuzzyFindCondition("434", "http://hl7.org/fhir/sid/icd-9", r) { pie.UpdateSliceValue("Stroke", 2) isFactor = true } else if fuzzyFindCondition("443", "http://hl7.org/fhir/sid/icd-9", r) { pie.UpdateSliceValue("Vascular Disease", 1) isFactor = true } case "Age": age := event.Value.(int) if age >= 65 && age < 75 { pie.UpdateSliceValue("Age", 1) isFactor = true } else if age >= 75 { pie.UpdateSliceValue("Age", 2) isFactor = true } } if hasAfib && isFactor { score := pie.TotalValues() percent := ScoreToStrokeRisk[score] results = append(results, plugin.RiskServiceCalculationResult{ AsOf: event.Date, Score: &score, ProbabilityDecimal: &percent, Pie: pie, }) } } return results, nil }
func (s *ServiceSuite) TestBuildRiskAssessmentBundle(c *C) { one, two, three, four := 1, 2, 3, 4 results := []plugin.RiskServiceCalculationResult{ { AsOf: time.Date(2000, 7, 14, 15, 59, 59, 999, time.UTC), Score: &one, Pie: plugin.NewPie(s.Server.URL + "/Patient/12345"), }, { AsOf: time.Date(2000, 7, 14, 16, 0, 0, 0, time.UTC), Score: &one, Pie: plugin.NewPie(s.Server.URL + "/Patient/12345"), }, { AsOf: time.Date(2012, 1, 1, 11, 0, 0, 0, time.UTC), Score: &three, Pie: plugin.NewPie(s.Server.URL + "/Patient/12345"), }, { AsOf: time.Date(2013, 1, 1, 11, 0, 0, 0, time.UTC), Score: &four, Pie: plugin.NewPie(s.Server.URL + "/Patient/12345"), }, { AsOf: time.Date(2014, 2, 3, 10, 0, 0, 0, time.UTC), Score: &two, Pie: plugin.NewPie(s.Server.URL + "/Patient/12345"), }, } pieBasisURL := "http://example.org/Pie" simplePlugin := assessments.NewSimplePlugin() bundle := buildRiskAssessmentBundle("12345", results, pieBasisURL, simplePlugin.Config()) c.Assert(bundle, NotNil) c.Assert(bundle.Type, Equals, "transaction") c.Assert(bundle.Entry, HasLen, 6) // First check the delete entry c.Assert(bundle.Entry[0].FullUrl, Equals, "") c.Assert(bundle.Entry[0].Link, HasLen, 0) c.Assert(bundle.Entry[0].Resource, IsNil) c.Assert(bundle.Entry[0].Response, IsNil) c.Assert(bundle.Entry[0].Search, IsNil) c.Assert(bundle.Entry[0].Request.Method, Equals, "DELETE") delURL := bundle.Entry[0].Request.Url c.Assert(strings.HasPrefix(delURL, "RiskAssessment?"), Equals, true) delValues, err := url.ParseQuery(strings.TrimPrefix(delURL, "RiskAssessment?")) util.CheckErr(err) c.Assert(delValues, HasLen, 2) c.Assert(delValues.Get("patient"), Equals, "12345") c.Assert(delValues.Get("method"), Equals, "http://interventionengine.org/risk-assessments|Simple") // Now check the post entries for i := 1; i < len(bundle.Entry); i++ { entry := bundle.Entry[i] result := results[i-1] c.Assert(entry.FullUrl, Equals, "") c.Assert(entry.Link, HasLen, 0) c.Assert(entry.Response, IsNil) c.Assert(entry.Search, IsNil) c.Assert(entry.Request.Method, Equals, "POST") c.Assert(entry.Request.Url, Equals, "RiskAssessment") c.Assert(entry.Resource, NotNil) ra, ok := entry.Resource.(*models.RiskAssessment) c.Assert(ok, Equals, true) c.Assert(ra.Id, Equals, "") c.Assert(ra.Basis, HasLen, 1) c.Assert(ra.Basis[0].Reference, Equals, pieBasisURL+"/"+result.Pie.Id.Hex()) c.Assert(ra.Date.Time, DeepEquals, result.AsOf) c.Assert(ra.Date.Precision, Equals, models.Precision(models.Timestamp)) c.Assert(*ra.Method, DeepEquals, simplePlugin.Config().Method) c.Assert(ra.Prediction, HasLen, 1) c.Assert(*ra.Prediction[0].Outcome, DeepEquals, simplePlugin.Config().PredictedOutcome) c.Assert(*ra.Prediction[0].ProbabilityDecimal, Equals, float64(*result.Score)) c.Assert(ra.Subject.Reference, Equals, "Patient/12345") } }