Esempio n. 1
0
func main() {
	// Check for a linked MongoDB container if we are running in Docker
	mongoHost := os.Getenv("MONGO_PORT_27017_TCP_ADDR")
	if mongoHost == "" {
		mongoHost = "localhost"
	}
	registerURL := flag.String("registerURL", "", "Register a FHIR Subscription to the specified URL")
	registerENV := flag.String("registerENV", "", "Register a FHIR Subscription to the the Docker environment variable IE_PORT_3001_TCP*")
	flag.Parse()
	parsedURL := *registerURL
	if parsedURL != "" {
		registerServer(parsedURL)
	}
	if registerENV != nil {
		registerServer(fmt.Sprintf("http://%s:%s", os.Getenv("IE_PORT_3001_TCP_ADDR"), os.Getenv("IE_PORT_3001_TCP_PORT")))
	}

	e := echo.New()
	session, err := mgo.Dial(mongoHost)
	if err != nil {
		panic("Can't connect to the database")
	}
	defer session.Close()

	basePieURL := discoverSelf() + "pies"
	db := session.DB("riskservice")
	svc := service.NewReferenceRiskService(db)
	svc.RegisterPlugin(assessments.NewCHA2DS2VAScPlugin())
	svc.RegisterPlugin(assessments.NewSimplePlugin())
	fnDelayer := server.NewFunctionDelayer(3 * time.Second)
	server.RegisterRoutes(e, db, basePieURL, svc, fnDelayer)
	e.Use(middleware.Logger())
	e.Run(":9000")
}
func (s *ServiceSuite) TestBuildNARiskAssessmentBundle(c *C) {
	simplePlugin := assessments.NewSimplePlugin()
	bundle := buildNARiskAssessmentBundle("12345", simplePlugin.Config())

	c.Assert(bundle, NotNil)
	c.Assert(bundle.Type, Equals, "transaction")
	c.Assert(bundle.Entry, HasLen, 2)

	// First check the delete entry
	entry := bundle.Entry[0]
	c.Assert(entry.FullUrl, Equals, "")
	c.Assert(entry.Link, HasLen, 0)
	c.Assert(entry.Resource, IsNil)
	c.Assert(entry.Response, IsNil)
	c.Assert(entry.Search, IsNil)
	c.Assert(entry.Request.Method, Equals, "DELETE")
	delURL := entry.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 NA post entry
	entry = bundle.Entry[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, 0)
	c.Assert(time.Now().Sub(ra.Date.Time) < time.Minute, Equals, true)
	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].ProbabilityCodeableConcept.Coding, HasLen, 1)
	c.Assert(ra.Prediction[0].ProbabilityCodeableConcept.Coding[0].System, Equals, "http://snomed.info/sct")
	c.Assert(ra.Prediction[0].ProbabilityCodeableConcept.Coding[0].Code, Equals, "385432009")
	c.Assert(ra.Prediction[0].ProbabilityCodeableConcept.Text, Equals, "Not applicable")
	c.Assert(ra.Subject.Reference, Equals, "Patient/12345")
}
func (s *ServiceSuite) TestGetRequiredDataQueryURLForCHADSandSimple(c *C) {
	s.Service.RegisterPlugin(assessments.NewCHA2DS2VAScPlugin())
	s.Service.RegisterPlugin(assessments.NewSimplePlugin())
	qURL, err := s.Service.getRequiredDataQueryURL("12345", "http://example.org/fhir")
	util.CheckErr(err)
	c.Assert(strings.HasPrefix(qURL, "http://example.org/fhir/Patient?"), Equals, true)
	qURL2, _ := url.Parse(qURL)
	c.Assert(qURL2.Query()["_revinclude"], HasLen, 2)
	var cFound, mFound bool
	for _, v := range qURL2.Query()["_revinclude"] {
		if v == "Condition:patient" {
			cFound = true
		} else if v == "MedicationStatement:patient" {
			mFound = true
		}
	}
	c.Assert(cFound, Equals, true)
	c.Assert(mFound, Equals, true)
}
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")
	}
}
func (s *ServiceSuite) TestEndToEndCalculations(c *C) {
	// Get the test data for Chad Chadworth (for the CHADS test -- get it?)
	data, err := os.Open("fixtures/chad_chadworth_bundle.json")
	util.CheckErr(err)
	defer data.Close()

	// Store Chad Chadworth's information to Mongo
	res, err := http.Post(s.Server.URL+"/", "application/json", data)
	util.CheckErr(err)
	defer res.Body.Close()

	// Get the response so we can pull out the patient ID
	decoder := json.NewDecoder(res.Body)
	responseBundle := new(models.Bundle)
	err = decoder.Decode(responseBundle)
	util.CheckErr(err)
	patientID := responseBundle.Entry[0].Resource.(*models.Patient).Id

	// Confirm there are no risk assessments or pies
	raCollection := s.Database.C("riskassessments")
	count, err := raCollection.Count()
	util.CheckErr(err)
	c.Assert(count, Equals, 0)
	piesCollection := s.Database.C("pies")
	count, err = piesCollection.Count()
	util.CheckErr(err)
	c.Assert(count, Equals, 0)

	// Now register the plugins and request the calculation!
	s.Service.RegisterPlugin(assessments.NewCHA2DS2VAScPlugin())
	s.Service.RegisterPlugin(assessments.NewSimplePlugin())

	err = s.Service.Calculate(patientID, s.Server.URL, s.Server.URL+"/pies")
	util.CheckErr(err)

	count, err = raCollection.Find(bson.M{"method.coding.code": "CHADS"}).Count()
	util.CheckErr(err)
	c.Assert(count, Equals, 4)
	count, err = raCollection.Find(bson.M{"method.coding.code": "Simple"}).Count()
	util.CheckErr(err)
	c.Assert(count, Equals, 4)
	count, err = piesCollection.Count()
	util.CheckErr(err)
	c.Assert(count, Equals, 8)

	var ras []models.RiskAssessment
	err = raCollection.Find(bson.M{"method.coding.code": "CHADS"}).Sort("date.time").All(&ras)
	util.CheckErr(err)

	loc := time.FixedZone("-0500", -5*60*60)
	s.checkCHADSRiskAssessment(c, &ras[0], patientID, time.Date(2012, time.September, 20, 8, 0, 0, 0, loc), 1.3, false)
	s.checkCHADSPie(c, &ras[0], patientID, 0, 0, 0, 0, 0, 1, 0)
	s.checkCHADSRiskAssessment(c, &ras[1], patientID, time.Date(2013, time.September, 2, 10, 0, 0, 0, loc), 2.2, false)
	s.checkCHADSPie(c, &ras[1], patientID, 0, 1, 0, 0, 0, 1, 0)
	s.checkCHADSRiskAssessment(c, &ras[2], patientID, time.Date(2014, time.January, 17, 20, 35, 0, 0, loc), 4.0, false)
	s.checkCHADSPie(c, &ras[2], patientID, 0, 1, 0, 2, 0, 1, 0)
	s.checkCHADSRiskAssessment(c, &ras[3], patientID, time.Date(2015, time.September, 2, 0, 0, 0, 0, loc), 6.7, true)
	s.checkCHADSPie(c, &ras[3], patientID, 0, 1, 0, 2, 0, 2, 0)

	ras = nil
	err = raCollection.Find(bson.M{"method.coding.code": "Simple"}).Sort("date.time").All(&ras)
	util.CheckErr(err)

	s.checkSimpleRiskAssessment(c, &ras[0], patientID, time.Date(2012, time.September, 20, 8, 0, 0, 0, loc), 1, false)
	s.checkSimplePie(c, &ras[0], patientID, 1, 0)
	s.checkSimpleRiskAssessment(c, &ras[1], patientID, time.Date(2013, time.September, 2, 10, 0, 0, 0, loc), 3, false)
	s.checkSimplePie(c, &ras[1], patientID, 2, 1)
	s.checkSimpleRiskAssessment(c, &ras[2], patientID, time.Date(2014, time.January, 17, 20, 35, 0, 0, loc), 4, false)
	s.checkSimplePie(c, &ras[2], patientID, 3, 1)
	s.checkSimpleRiskAssessment(c, &ras[3], patientID, time.Date(2014, time.January, 17, 20, 40, 0, 0, loc), 3, true)
	s.checkSimplePie(c, &ras[3], patientID, 2, 1)
}
func (s *ServiceSuite) TestEndToEndCalculationsWithNACHADS(c *C) {
	// Get the test data for Chad Chadworth (for the CHADS test -- get it?)
	data, err := os.Open("fixtures/chad_chadworth_bundle.json")
	util.CheckErr(err)
	defer data.Close()

	// Remove the AFib entry (index 5) so he is Not Applicable
	bundle := new(models.Bundle)
	decoder := json.NewDecoder(data)
	err = decoder.Decode(bundle)
	util.CheckErr(err)
	bundle.Entry = append(bundle.Entry[:5], bundle.Entry[6:]...)
	b, err := json.Marshal(bundle)
	util.CheckErr(err)

	// Store Chad Chadworth's modified information to Mongo
	res, err := http.Post(s.Server.URL+"/", "application/json", bytes.NewBuffer(b))
	util.CheckErr(err)
	defer res.Body.Close()

	// Get the response so we can pull out the patient ID
	decoder = json.NewDecoder(res.Body)
	responseBundle := new(models.Bundle)
	err = decoder.Decode(responseBundle)
	util.CheckErr(err)
	patientID := responseBundle.Entry[0].Resource.(*models.Patient).Id

	// Confirm there are no risk assessments or pies
	raCollection := s.Database.C("riskassessments")
	count, err := raCollection.Count()
	util.CheckErr(err)
	c.Assert(count, Equals, 0)
	piesCollection := s.Database.C("pies")
	count, err = piesCollection.Count()
	util.CheckErr(err)
	c.Assert(count, Equals, 0)

	// Now register the plugins and request the calculation!
	s.Service.RegisterPlugin(assessments.NewCHA2DS2VAScPlugin())
	s.Service.RegisterPlugin(assessments.NewSimplePlugin())

	err = s.Service.Calculate(patientID, s.Server.URL, s.Server.URL+"/pies")
	util.CheckErr(err)

	count, err = raCollection.Find(bson.M{"method.coding.code": "CHADS"}).Count()
	util.CheckErr(err)
	c.Assert(count, Equals, 1)
	count, err = raCollection.Find(bson.M{"method.coding.code": "Simple"}).Count()
	util.CheckErr(err)
	c.Assert(count, Equals, 4)
	count, err = piesCollection.Count()
	util.CheckErr(err)
	c.Assert(count, Equals, 4)

	var ras []models.RiskAssessment
	err = raCollection.Find(bson.M{"method.coding.code": "CHADS"}).All(&ras)
	util.CheckErr(err)

	// Check that the CHADS is not applicable
	c.Assert(ras, HasLen, 1)
	ra := ras[0]
	c.Assert(ra.Subject.Reference, Equals, "Patient/"+patientID)
	c.Assert(ra.Method.MatchesCode("http://interventionengine.org/risk-assessments", "CHADS"), Equals, true)
	c.Assert(time.Now().Sub(ra.Date.Time) < time.Minute, Equals, true)
	c.Assert(ra.Prediction, HasLen, 1)
	c.Assert(ra.Prediction[0].Outcome.Text, Equals, "Stroke")
	c.Assert(ra.Prediction[0].ProbabilityCodeableConcept.Coding, HasLen, 1)
	c.Assert(ra.Prediction[0].ProbabilityCodeableConcept.Coding[0].System, Equals, "http://snomed.info/sct")
	c.Assert(ra.Prediction[0].ProbabilityCodeableConcept.Coding[0].Code, Equals, "385432009")
	c.Assert(ra.Prediction[0].ProbabilityCodeableConcept.Text, Equals, "Not applicable")
	c.Assert(ra.Basis, HasLen, 0)
	c.Assert(ra.Meta.Tag, HasLen, 1)
	c.Assert(ra.Meta.Tag[0], DeepEquals, models.Coding{System: "http://interventionengine.org/tags/", Code: "MOST_RECENT"})

	// Simple should still be good
	ras = nil
	err = raCollection.Find(bson.M{"method.coding.code": "Simple"}).Sort("date.time").All(&ras)
	util.CheckErr(err)

	loc := time.FixedZone("-0500", -5*60*60)
	s.checkSimpleRiskAssessment(c, &ras[0], patientID, time.Date(2012, time.September, 20, 8, 0, 0, 0, loc), 1, false)
	s.checkSimplePie(c, &ras[0], patientID, 1, 0)
	s.checkSimpleRiskAssessment(c, &ras[1], patientID, time.Date(2013, time.September, 2, 10, 0, 0, 0, loc), 3, false)
	s.checkSimplePie(c, &ras[1], patientID, 2, 1)
	s.checkSimpleRiskAssessment(c, &ras[2], patientID, time.Date(2014, time.January, 17, 20, 35, 0, 0, loc), 4, false)
	s.checkSimplePie(c, &ras[2], patientID, 3, 1)
	s.checkSimpleRiskAssessment(c, &ras[3], patientID, time.Date(2014, time.January, 17, 20, 40, 0, 0, loc), 3, true)
	s.checkSimplePie(c, &ras[3], patientID, 2, 1)
}