/// <summary> /// Update conflict records /// </summary> /// <param name="oldIdentifier">The old HSR event id</param> /// <param name="recordId">The new HSR event id</param> public void ObsoleteConflicts(VersionedDomainIdentifier recordId) { // First, we load the survivor if (recordId.Domain != ApplicationContext.ConfigurationService.OidRegistrar.GetOid(ClientRegistryOids.REGISTRATION_EVENT).Oid) { throw new ArgumentException(String.Format("Must be drawn from the '{0}' domain", ApplicationContext.ConfigurationService.OidRegistrar.GetOid(ClientRegistryOids.REGISTRATION_EVENT).Oid), "recordId"); } // Now persist the replacement IDbConnection conn = DatabasePersistenceService.ConnectionManager.GetConnection(); try { using (IDbCommand cmd = DbUtil.CreateCommandStoredProc(conn, null)) { cmd.CommandText = "obslt_mrg"; cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "old_hsr_id_in", DbType.Decimal, Decimal.Parse(recordId.Identifier))); cmd.ExecuteNonQuery(); } } finally { DatabasePersistenceService.ConnectionManager.ReleaseConnection(conn); } }
/// <summary> /// Create an HSR record /// </summary> private VersionedDomainIdentifier CreateHSRRecord(IDbConnection conn, IDbTransaction tx, ChangeSummary hsr) { IDbCommand cmd = DbUtil.CreateCommandStoredProc(conn, tx); cmd.CommandText = "crt_hsr"; // Get the terminology service ISystemConfigurationService iscs = ApplicationContext.ConfigurationService; //ApplicationContext.Current.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService; // Parameters // classifier = 0x400 = Change Summary cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "hsr_cls_in", DbType.Decimal, RegistrationEventType.ComponentEvent | RegistrationEventType.Revise)); // event type code cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "evt_typ_cd_id_in", DbType.Decimal, DbUtil.CreateCodedValue(conn, tx, hsr.ChangeType))); // refuted indicator cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "refuted_ind_in", DbType.Boolean, false)); decimal?efftTimeId = null; if (hsr.EffectiveTime != null) { efftTimeId = DbUtil.CreateTimeset(conn, tx, hsr.EffectiveTime); } cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "efft_ts_set_id_in", DbType.Decimal, efftTimeId == null ? (object)DBNull.Value : efftTimeId.Value)); // status code cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "status_cs_in", DbType.Decimal, hsr.Status == null ? (object)DBNull.Value : (int)hsr.Status)); // authored time cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "aut_utc_in", DbType.DateTime, hsr.Timestamp == default(DateTime) ? (object)DBNull.Value : hsr.Timestamp)); // language code cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "lang_cs_in", DbType.String, hsr.LanguageCode)); // Execute the command IDataReader resultRdr = cmd.ExecuteReader(); try { // Create the return value VersionedDomainIdentifier id = new VersionedDomainIdentifier(); if (!resultRdr.Read()) { return(null); } id.Version = Convert.ToString(resultRdr["VRSN_ID"]); id.Identifier = Convert.ToString(resultRdr["ID"]); id.Domain = iscs.OidRegistrar.GetOid(ClientRegistryOids.EVENT_OID).Oid; return(id); } finally { resultRdr.Close(); } }
/// <summary> /// Resolve an item /// </summary> /// <param name="sourceId"></param> public void Resolve(decimal sourceId) { // Get all Services IAuditorService auditSvc = ApplicationContext.CurrentContext.GetService(typeof(IAuditorService)) as IAuditorService; IClientRegistryMergeService mergeSvc = ApplicationContext.CurrentContext.GetService(typeof(IClientRegistryMergeService)) as IClientRegistryMergeService; // Audit message AuditData auditMessage = this.ConstructAuditData(ActionType.Update, EventIdentifierType.ApplicationActivity); auditMessage.EventTypeCode = new CodeValue("ADM_Resolve"); try { // Prepare merge VersionedDomainIdentifier resolveId = new VersionedDomainIdentifier() { Domain = ApplicationContext.ConfigurationService.OidRegistrar.GetOid("REG_EVT").Oid, Identifier = sourceId.ToString() }; // Merge mergeSvc.MarkResolved(resolveId); auditMessage.AuditableObjects.Add(new AuditableObject() { IDTypeCode = AuditableObjectIdType.ReportNumber, LifecycleType = AuditableObjectLifecycle.Verification, ObjectId = string.Format("{0}^^^&{1}&ISO", resolveId.Identifier, resolveId.Domain), Role = AuditableObjectRole.Job, Type = AuditableObjectType.SystemObject }); } catch (Exception e) { auditMessage.Outcome = OutcomeIndicator.EpicFail; Trace.TraceError("Could not execute Merge : {0}", e.ToString()); #if DEBUG throw new FaultException(new FaultReason(e.ToString()), new FaultCode(e.GetType().Name)); #else throw new FaultException(new FaultReason(e.Message), new FaultCode(e.GetType().Name)); #endif } finally { if (auditSvc != null) { auditSvc.SendAudit(auditMessage); } } }
/// <summary> /// Aync mark conflicts code /// </summary> private void MarkConflictsAsync(object state) { Trace.TraceInformation("Performing fuzzy conflict check asynchronously"); try { VersionedDomainIdentifier vid = state as VersionedDomainIdentifier; RegistrationEvent hsrEvent = this.GetContainer(vid, true) as RegistrationEvent; var pid = this.m_clientRegistryMerge.FindFuzzyConflicts(hsrEvent); Trace.TraceInformation("Post-Update Record matched with {0} records", pid.Count()); if (pid.Count() > 0) { this.m_clientRegistryMerge.MarkConflicts(hsrEvent.AlternateIdentifier, pid); } } catch (Exception e) { Trace.TraceError(e.ToString()); } }
/// <summary> /// Find conflicts using fuzzy match /// </summary> public IEnumerable <VersionedDomainIdentifier> FindFuzzyConflicts(RegistrationEvent registration) { var registrationService = this.Context.GetService(typeof(IDataRegistrationService)) as IDataRegistrationService; var persistenceService = this.Context.GetService(typeof(IDataPersistenceService)) as IDataPersistenceService; var clientRegistryConfigService = this.Context.GetService(typeof(IClientRegistryConfigurationService)) as IClientRegistryConfigurationService; VersionedDomainIdentifier[] pid = null; var subject = registration.FindComponent(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf) as Person; QueryParameters qp = new QueryParameters() { Confidence = 1.0f, MatchingAlgorithm = MatchAlgorithm.Exact, MatchStrength = MatchStrength.Exact }; if (subject.Status != StatusType.Active) { return(pid); } var patientQuery = new QueryEvent(); var registrationEvent = new RegistrationEvent(); patientQuery.Add(registrationEvent, "SUBJ", HealthServiceRecordSiteRoleType.SubjectOf, null); patientQuery.Add(qp, "FLT", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.FilterOf, null); // Create merge filter for fuzzy match var ssubject = clientRegistryConfigService.CreateMergeFilter(subject); if (ssubject != null) // Minimum criteria was met { registrationEvent.Add(ssubject, "SUBJ", HealthServiceRecordSiteRoleType.SubjectOf, null); pid = registrationService.QueryRecord(patientQuery); } else { pid = new VersionedDomainIdentifier[0]; } // Now, look for all with an assigning authority if (pid.Length > 0) { pid = pid.Where(o => !(o.Identifier == registration.Id.ToString() && o.Domain == ApplicationContext.ConfigurationService.OidRegistrar.GetOid(ClientRegistryOids.REGISTRATION_EVENT).Oid)).ToArray(); // Load each match quickly and ensure that they don't already have a // different identifier from an assigning authority (not CR_CID) provided // in the registration method. For example, if John Smith, Male, 1984-05-22, 123 Main Street West is // registered from system X with ID 102 , and a subsequent registration message for John Smith, Male, 1984-05-22, 123 Main Street West // is received from system X with ID 104, it is pretty much assured they aren't the same person. If however the // latter message came from system Y with ID 104, then the two should be considered a match. ssubject.AlternateIdentifiers = new List <DomainIdentifier>(); List <VersionedDomainIdentifier> exclude = new List <VersionedDomainIdentifier>(); foreach (var altId in subject.AlternateIdentifiers) { if (altId.Domain != ApplicationContext.ConfigurationService.OidRegistrar.GetOid(ClientRegistryOids.CLIENT_CRID).Oid) { var oidData = ApplicationContext.ConfigurationService.OidRegistrar.FindData(altId.Domain); if (oidData == null || oidData.Attributes.Find(o => o.Key == "IsUniqueIdentifier").Value == null || Boolean.Parse(oidData.Attributes.Find(o => o.Key == "IsUniqueIdentifier").Value)) { ssubject.AlternateIdentifiers.Add(new DomainIdentifier() { Domain = altId.Domain }); } } } foreach (var p in pid) { var re = persistenceService.GetContainer(p, true) as RegistrationEvent; var pat = re.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf) as Person; if (pat.Id == subject.Id || pat.Status != StatusType.Active) // same person { exclude.Add(re.AlternateIdentifier); } else if (pat.AlternateIdentifiers.Exists(o => ssubject.AlternateIdentifiers.Exists(r => r.Domain == o.Domain))) { exclude.Add(re.AlternateIdentifier); } } pid = pid.Where(o => !exclude.Exists(i => i.Domain == o.Domain && i.Identifier == o.Identifier)).ToArray(); } return(pid); }
/// <summary> /// Perform a merge /// </summary> public Core.ComponentModel.RegistrationEvent Merge(decimal[] sourceIds, decimal targetId) { // Get all Services IAuditorService auditSvc = ApplicationContext.CurrentContext.GetService(typeof(IAuditorService)) as IAuditorService; IDataPersistenceService repSvc = ApplicationContext.CurrentContext.GetService(typeof(IDataPersistenceService)) as IDataPersistenceService; IClientRegistryMergeService mergeSvc = ApplicationContext.CurrentContext.GetService(typeof(IClientRegistryMergeService)) as IClientRegistryMergeService; // Audit message List <AuditData> auditMessages = new List <AuditData>(); try { // Prepare merge List <VersionedDomainIdentifier> domainId = new List <VersionedDomainIdentifier>(sourceIds.Length); foreach (var srcId in sourceIds) { domainId.Add(new VersionedDomainIdentifier() { Domain = ApplicationContext.ConfigurationService.OidRegistrar.GetOid("REG_EVT").Oid, Identifier = srcId.ToString() }); var am = ConstructAuditData(ActionType.Delete, EventIdentifierType.Import); am.EventTypeCode = new CodeValue("ADM_Merge"); am.AuditableObjects.Add(new AuditableObject() { IDTypeCode = AuditableObjectIdType.ReportNumber, LifecycleType = AuditableObjectLifecycle.LogicalDeletion, ObjectId = String.Format("{0}^^^&{1}&ISO", srcId, ApplicationContext.ConfigurationService.OidRegistrar.GetOid("REG_EVT").Oid), Role = AuditableObjectRole.MasterFile, Type = AuditableObjectType.SystemObject }); auditMessages.Add(am); } VersionedDomainIdentifier survivorId = new VersionedDomainIdentifier() { Domain = ApplicationContext.ConfigurationService.OidRegistrar.GetOid("REG_EVT").Oid, Identifier = targetId.ToString() }; var updateAudit = ConstructAuditData(ActionType.Update, EventIdentifierType.Import); updateAudit.EventTypeCode = new CodeValue("ADM_Merge"); updateAudit.AuditableObjects.Add(new AuditableObject() { IDTypeCode = AuditableObjectIdType.ReportNumber, LifecycleType = AuditableObjectLifecycle.Amendment, ObjectId = String.Format("{0}^^^&{1}&ISO", survivorId.Identifier, survivorId.Domain), Role = AuditableObjectRole.MasterFile, Type = AuditableObjectType.SystemObject }); auditMessages.Add(updateAudit); // Merge mergeSvc.Resolve(domainId, survivorId, DataPersistenceMode.Production); // Now load return(repSvc.GetContainer(survivorId, true) as RegistrationEvent); } catch (Exception e) { foreach (var am in auditMessages) { am.Outcome = OutcomeIndicator.EpicFail; } Trace.TraceError("Could not execute Merge : {0}", e.ToString()); #if DEBUG throw new FaultException(new FaultReason(e.ToString()), new FaultCode(e.GetType().Name)); #else throw new FaultException(new FaultReason(e.Message), new FaultCode(e.GetType().Name)); #endif } finally { if (auditSvc != null) { foreach (var am in auditMessages) { auditSvc.SendAudit(am); } } } }
/// <summary> /// Get a specific conflict /// </summary> public ConflictCollection GetConflict(decimal id) { // Get all Services IAuditorService auditSvc = ApplicationContext.CurrentContext.GetService(typeof(IAuditorService)) as IAuditorService; IDataPersistenceService repSvc = ApplicationContext.CurrentContext.GetService(typeof(IDataPersistenceService)) as IDataPersistenceService; IClientRegistryMergeService mergeSvc = ApplicationContext.CurrentContext.GetService(typeof(IClientRegistryMergeService)) as IClientRegistryMergeService; // Audit message AuditData audit = this.ConstructAuditData(ActionType.Read, EventIdentifierType.Export); audit.EventTypeCode = new CodeValue("ADM_GetConflict"); try { var vid = new VersionedDomainIdentifier() { Domain = ApplicationContext.ConfigurationService.OidRegistrar.GetOid("REG_EVT").Oid, Identifier = id.ToString() }; // Get all with a merge var mergeResults = mergeSvc.GetConflicts(vid); var retVal = new ConflictCollection(); // Construct the return, and load match var conf = new Conflict() { Source = repSvc.GetContainer(vid, true) as RegistrationEvent }; // Add audit data audit.AuditableObjects.Add(new AuditableObject() { IDTypeCode = AuditableObjectIdType.ReportNumber, LifecycleType = AuditableObjectLifecycle.Export, ObjectId = String.Format("{0}^^^&{1}&ISO", conf.Source.AlternateIdentifier.Identifier, conf.Source.AlternateIdentifier.Domain), Role = AuditableObjectRole.MasterFile, Type = AuditableObjectType.SystemObject, QueryData = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("loadFast=false")) }); // Load the matches foreach (var match in mergeResults) { var matchRecord = repSvc.GetContainer(match, true) as RegistrationEvent; conf.Match.Add(matchRecord); // Add audit data audit.AuditableObjects.Add(new AuditableObject() { IDTypeCode = AuditableObjectIdType.ReportNumber, LifecycleType = AuditableObjectLifecycle.Export, ObjectId = String.Format("{0}^^^&{1}&ISO", matchRecord.AlternateIdentifier.Identifier, matchRecord.AlternateIdentifier.Domain), Role = AuditableObjectRole.MasterFile, Type = AuditableObjectType.SystemObject, QueryData = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("loadFast=false")) }); } retVal.Conflict.Add(conf); return(retVal); } catch (Exception e) { Trace.TraceError("Could not execute GetConflicts : {0}", e.ToString()); audit.Outcome = OutcomeIndicator.EpicFail; #if DEBUG throw new FaultException(new FaultReason(e.ToString()), new FaultCode(e.GetType().Name)); #else throw new FaultException(new FaultReason(e.Message), new FaultCode(e.GetType().Name)); #endif } finally { if (auditSvc != null) { auditSvc.SendAudit(audit); } } }
/// <summary> /// Create the HSR record /// </summary> internal VersionedDomainIdentifier CreateHSRRecord(IDbConnection conn, IDbTransaction tx, RegistrationEvent hsr) { IDbCommand cmd = DbUtil.CreateCommandStoredProc(conn, tx); cmd.CommandText = "crt_hsr"; // Get the terminology service //ITerminologyService its = ApplicationContext.CurrentContext.GetService(typeof(ITerminologyService)) as ITerminologyService; ISystemConfigurationService iscs = ApplicationContext.ConfigurationService; //ApplicationContext.Current.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService; // Validate the language code //if (its != null) //{ // var validationError = its.Validate(hsr.LanguageCode, null, CodeSystemName.ISO639); // if (validationError.Outcome != MARC.HI.EHRS.SVC.Core.Terminology.ValidationOutcome.ValidWithWarning && // validationError.Outcome != MARC.HI.EHRS.SVC.Core.Terminology.ValidationOutcome.Valid) // throw new ConstraintException("Language MUST be a valid ISO639 Country code in the format XX-XX"); //} // Parameters // classifier cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "hsr_cls_in", DbType.Decimal, (int)hsr.EventClassifier)); // event type code cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "evt_typ_cd_id_in", DbType.Decimal, DbUtil.CreateCodedValue(conn, tx, hsr.EventType))); // refuted indicator cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "refuted_ind_in", DbType.Boolean, hsr.Refuted)); decimal?efftTimeId = null; if (hsr.EffectiveTime != null) { efftTimeId = DbUtil.CreateTimeset(conn, tx, hsr.EffectiveTime); } cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "efft_ts_set_id_in", DbType.Decimal, efftTimeId == null ? (object)DBNull.Value : efftTimeId.Value)); // status code cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "status_cs_in", DbType.Decimal, hsr.Status == null ? (object)DBNull.Value : (int)hsr.Status)); // authored time cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "aut_utc_in", DbType.DateTime, hsr.Timestamp == default(DateTime) ? (object)DBNull.Value : hsr.Timestamp)); // language code cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "lang_cs_in", DbType.String, hsr.LanguageCode)); // Execute the command IDataReader resultRdr = cmd.ExecuteReader(); try { // Create the return value VersionedDomainIdentifier id = new VersionedDomainIdentifier(); if (!resultRdr.Read()) { return(null); } id.Version = Convert.ToString(resultRdr["VRSN_ID"]); id.Identifier = Convert.ToString(resultRdr["ID"]); id.Domain = iscs.OidRegistrar.GetOid(ClientRegistryOids.REGISTRATION_EVENT).Oid; return(id); } finally { resultRdr.Close(); } }
/// <summary> /// Get record /// </summary> private HealthServiceRecordContainer GetRecord(VersionedDomainIdentifier recordId, List <IResultDetail> dtls, RegistryQueryRequest qd) { try { // Can't find persistence if (this.m_persistenceService == null) { dtls.Add(new PersistenceResultDetail(ResultDetailType.Error, "Couldn't locate an implementation of a PersistenceService object, storage is aborted", null)); throw new Exception("Cannot de-persist records"); } // Read the record from the DB var result = this.m_persistenceService.GetContainer(recordId, qd.IsSummary) as HealthServiceRecordContainer; // Does this result match what we're looking for? if (result == null) { return(null); // next record } // Calculate the matching algorithm Person subject; if (result is Person) { subject = result as Person; } else { subject = result.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf) as Person; } // Remove all but the alternate identifiers specifed in the query if (qd.TargetDomain != null && subject != null) { subject.AlternateIdentifiers.RemoveAll(o => !qd.TargetDomain.Exists(t => t.Domain.Equals(o.Domain))); if (subject.AlternateIdentifiers.Count == 0) { return(null); } } if (subject != null && qd.QueryRequest != null) { var filter = qd.QueryRequest.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf); while (!(filter is Person) && filter != null) { filter = (filter as HealthServiceRecordContainer).FindComponent(HealthServiceRecordSiteRoleType.SubjectOf); } var confidence = (subject as Person).Confidence(filter as Person); if (confidence.Confidence < qd.MinimumDegreeMatch) { return(null); } subject.Add(confidence, "CONF", HealthServiceRecordSiteRoleType.ComponentOf | HealthServiceRecordSiteRoleType.CommentOn, null); } // Mask if (this.m_policyService != null) { var dte = new List <SVC.Core.Issues.DetectedIssue>(); result = this.m_policyService.ApplyPolicies(qd.QueryRequest, result, dte) as HealthServiceRecordContainer; foreach (var itm in dte) { dtls.Add(new DetectedIssueResultDetail( ResultDetailType.Warning, itm.Text, (string)null, itm)); } } return(result); } catch (Exception ex) { dtls.Add(new PersistenceResultDetail(ResultDetailType.Error, ex.Message, ex)); return(null); } }