Ejemplo n.º 1
0
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")
	}
}