예제 #1
0
        /// <summary>
        /// Retrieve the list of clinical problems for a patient encounter.
        /// </summary>
        /// <param name="id">encounter id</param>
        /// <returns>An xml serializtion string of a list of clinical problems</returns>
        string IDecisionSupportService.RetrieveProblemList(int id)
        {
            var encounter = db.EncounterContextItemDefinitionFind(id);
            if (encounter == null)
                return "";
            try
            {
                var list = new List<ClinicalProblemInstanceSimpleObject>();

                foreach (var instance in AssociationHelper.SearchClinicalProblemInstance(encounter, db))
                {

                    var simpleObject = new ClinicalProblemInstanceSimpleObject()
                    {                        
                        Reports = new List<KeyValuePair<string, string>>(),
                        Facts = new List<string>(),
                        OrderSet = new List<string>(),
                        History = new List<string>()
                    };

                    simpleObject.Id = instance.Id;
                    simpleObject.Priority = instance.Priority;
                    simpleObject.State = instance.State;
                    if(instance.ClinicalProblemDefinition!=null && string.IsNullOrEmpty(instance.ClinicalProblemDefinition.Name)==false)
                        simpleObject.Name = instance.ClinicalProblemDefinition.Name;
                    if(instance.ChangeRecord!=null && instance.ChangeRecord.Count()>0)
                        simpleObject.TimeStamp = instance.ChangeRecord.ElementAt(0).TimeStamp.Value;
                    if (instance.ClinicalProblemDefinition != null && instance.ClinicalProblemDefinition.TriggerRule != null)
                        simpleObject.TriggerRule = RuleSetHelper.GetLaymanConditionString(instance.ClinicalProblemDefinition.TriggerRule.RuleSet);

                    foreach (var fact in instance.Facts)
                    {
                        var factString = string.IsNullOrEmpty(fact.ContextItemDefinition.ReferenceRange) ?
                            fact.ContextItemDefinition.Name + " = " + fact.ValueString() :
                            fact.ContextItemDefinition.Name + " = " + fact.ValueString() + " (参考范围:" + fact.ContextItemDefinition.ReferenceRange + ")";
                        simpleObject.Facts.Add(factString);
                    }

                    foreach (var report in instance.Reports)
                    {
                        simpleObject.Reports.Add(new KeyValuePair<string, string>(report.TimeStamp + " " + report.ReportType, report.URL));
                    }

                    // Don't need to show history for now.
                    //foreach (var record in instance.ChangeRecord)
                    //{
                    //    simpleObject.History.Add();
                    //}

                    int count = 0;
                    foreach (var order in db.MedicalOrderSet)
                    {
                        simpleObject.OrderContextItemDefinitionAdd(order.Name);
                        if (count++ > 10)
                            break;
                    }

                    list.Add(simpleObject);
                }                

                var obj = list as object;
                return SerializationHelper.Serialize(ref obj);
            }
            catch
            {
                return "";
            }
        }
예제 #2
0
        /// <summary>
        /// Innter Implemention of I/F Notify():
        /// void IDecisionSupportService.Notify(int id)
        /// </summary>
        /// <param name="id">event id</param>
        /// <returns>log items.
        /// For each log keypair, DateTime is timestamp, string is log content.</returns>
        public IEnumerable<KeyValuePair<DateTime, string>> Notify(int id)
        {
            var log = new List<KeyValuePair<DateTime, string>>();
            var sw = Stopwatch.StartNew();
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "[BEGIN]"));
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "Call I/F Notify(" + id + ")"));

            // TODO: Search EMR db and construct event, report, encounter, etc. in CDS db

            var evt = db.Event.Find(id);
            if (evt == null)
            {
                log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "Event with specified id doesnot exist. Exit funciton now."));
                return log;
            }

            var encounter = evt.Encounter;
            if (encounter == null)
            {
                log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "Associated encounter doesnot exist. Exit funciton now."));
                return log;
            }

            // Collect abnormal facts from the event
            //            
            var abnormalFacts = new List<Fact>();
            var relatedProblemInstances = new List<ClinicalProblemInstance>();
            var solvedSuspectProblemInstances = new List<ClinicalProblemInstance>();
            foreach (var x in evt.Report)
            {
                // Get facts from report and update to profile.
                var facts = (new FactServiceMocker()).GetFactsFromReportMock(x.Id, db).ToList();
                // TODO: var facts = factService.GetFactsFromReport(x.URL).ToList();
                facts.ForEach(y =>
                {
                    db.UpdateFactCache(encounter.Id, y);
                    if (y.ContextItemDefinition.Type == EnumContextItemType.ClinicalProblem.ToString() && y.IsAbnormal == false)
                    {
                        var instance = db.ClinicalProblemInstance.FirstOrDefault(z => z.State == EnumProblemState.New.ToString() && z.ClinicalProblemDefinition.Name == y.ContextItemDefinition.Name);
                        if (instance != null)
                        {
                            solvedSuspectProblemInstances.Add(instance);
                        }
                    }

                    // collect related problem instances associated with the fact. Later, use Distinct() to get full list.
                    foreach (var z in db.ClinicalProblemInstance)
                    {
                        if ((z.State == EnumProblemState.New.ToString() ||
                            z.State == EnumProblemState.ResolvedSuspected.ToString()) &&
                            z.ClinicalProblemDefinition.ContextItemDefinition.Contains(y.ContextItemDefinition, new PropertyComparer<ContextItemDefinition>("Id")))
                        {
                            relatedProblemInstances.Add(z);
                        }
                    }

                    // Collect abnormal facts as triggers
                    if (y.IsAbnormal == true)
                    {
                        abnormalFacts.Add(y);
                    }
                });
            };

            // Update problems that are suspected no longer existing.
            solvedSuspectProblemInstances.Distinct(new PropertyComparer<ClinicalProblemInstance>("Id")).ToList().ForEach(x =>
            {
                db.UpdateProblemState(x.Id, EnumProblemState.ResolvedSuspected.ToString(), EnumProblemStateChangeReason.CDS.ToString(), CurrentUser);
            });

            // Update related active problems with new report, for tracking.
            relatedProblemInstances.Distinct(new PropertyComparer<ClinicalProblemInstance>("Id")).ToList().ForEach(x =>
            {
                var result = ReEvaluate(x.Id);
                if (result.HasValue)
                {
                    if (result.Value == false)
                    {
                        db.UpdateProblemState(x.Id, EnumProblemState.ResolvedSuspected.ToString(), EnumProblemStateChangeReason.CDS.ToString(),CurrentUser);
                    }
                    else
                    {
                        db.UpdateProblemState(x.Id, x.State, EnumProblemStateChangeReason.CDS.ToString(),CurrentUser);
                    }
                }
            });

            if (abnormalFacts.Count <= 0)
            {
                log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "No abnormal facts were detected. Exit funciton now."));
                return log;
            }

            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, ""));
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, abnormalFacts.Count + " abnormal facts were detected:"));
            abnormalFacts.ForEach(x =>
            {
                log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, x.ContextItemDefinition.Name + " = " + x.ValueString()));
            });
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, ""));

            var triggerFacts = new List<Fact>();
            abnormalFacts.ForEach(x =>
            {
                if (x.ContextItemDefinition.Type==EnumContextItemType.ClinicalProblem.ToString()) // The fact itself is a problem. e.g. the fact comes from a diagnosis event.
                {
                    int ID = db.CreateClinicalProblemInstance(x.ContextItemDefinition.Name, encounter.Id, false);
                    if (ID != -1)
                    {
                        db.UpdateProblemState(ID, EnumProblemState.New.ToString(), EnumProblemStateChangeReason.CDS.ToString(), CurrentUser, true);
                        log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "!!! New Problem[" + x.ContextItemDefinition.Name + "] is detected directly from fact[" + x.ContextItemDefinition.Name + "=" + x.ValueString() + "]. (the fact itself is a problem)"));
                    }
                }
                else // The fact needs reasoning by rule engine to generate problem.
                {
                    triggerFacts.Add(x);
                }
            });

            //
            // Get candidate problems by abnormal facts
            //
            var candidateProblems = GetCandidateProblems(triggerFacts);

            if (candidateProblems.Count() <= 0)
            {
                log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "No candidate problems were detected. Exit function now."));
                return log;
            }

            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, ""));
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, candidateProblems.Count() + " candidate problems were detected:"));
            candidateProblems.ToList().ForEach(x =>
            {
                log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, x.Name));
            });
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, ""));


            //
            // Construct reasoning context for candidate problems
            //
            IEnumerable<ClinicalProblemDefinition> filteredProblems;
            Context reasoningContext;
            ConstructReasoningContext(encounter, candidateProblems, out filteredProblems, out reasoningContext);

            if (filteredProblems.Count() <= 0)
            {
                log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "Lacking reasoning facts for all candidate problems. Exit function now."));
                return log;
            }

            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, ""));
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "After checking data availability, " + filteredProblems.Count() + " problems can go through rule engine:"));
            filteredProblems.ToList().ForEach(x =>
            {
                log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, x.Name));
            });
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, ""));
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "Reasoning context contains: " + reasoningContext.GetAllKeys().Count() + " items:"));
            foreach (var x in reasoningContext.GetAllKeys())
            {
                log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, x + " = " + reasoningContext.GetValue(x)));
            }
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, ""));

            //
            // Reason by Rule Engine
            //
            var triggeredProblems = Reason(filteredProblems, reasoningContext);

            if (triggeredProblems != null && triggeredProblems.Count() > 0)
            {
                triggeredProblems.ToList().ForEach(x =>
                {
                    int ID = db.CreateClinicalProblemInstance(x, encounter.Id, true);
                    if (relatedProblemInstances.Any(y => y.Id == ID) == false // means the problem state is already updated above
                        && ID != -1)
                    {
                        db.UpdateProblemState(ID, EnumProblemState.New.ToString(), EnumProblemStateChangeReason.CDS.ToString(), CurrentUser);
                        var instance = db.ClinicalProblemInstance.Find(ID);
                        if (instance != null && instance.TriggerRule != null && string.IsNullOrEmpty(instance.TriggerRule.RuleSet) == false)
                            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "!!! New Problem[" + x + "] is detected from rule[" + RuleSetHelper.GetLaymanConditionString(instance.TriggerRule.RuleSet) + "]."));
                    }
                });
            }

            sw.Stop();
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, ""));
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "This I/F call consumes " + sw.ElapsedMilliseconds + " ms"));
            log.Add(new KeyValuePair<DateTime, string>(DateTime.Now, "[END]"));

            return log;
        }