/// <summary> /// Enrich the registration event /// </summary> internal void EnrichRegistrationEvent(IDbConnection conn, IDbTransaction tx, RegistrationEvent hsrEvent) { decimal tryDec; ISystemConfigurationService configService = ApplicationContext.ConfigurationService; // Create a query based on the person Person subject = hsrEvent.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf) as Person; if (subject.AlternateIdentifiers.Exists(o => o.Domain == configService.OidRegistrar.GetOid(ClientRegistryOids.CLIENT_CRID).Oid)) { subject = new Person() { AlternateIdentifiers = new List <DomainIdentifier>(subject.AlternateIdentifiers.FindAll(o => o.Domain == configService.OidRegistrar.GetOid(ClientRegistryOids.CLIENT_CRID).Oid)), Status = StatusType.Normal } } ; else if (subject.AlternateIdentifiers.Exists(o => o is AuthorityAssignedDomainIdentifier)) { subject = new Person() { AlternateIdentifiers = new List <DomainIdentifier>(subject.AlternateIdentifiers.FindAll(o => o is AuthorityAssignedDomainIdentifier)), Status = StatusType.Normal } } ; else { subject = new Person() { AlternateIdentifiers = new List <DomainIdentifier>(subject.AlternateIdentifiers), Status = StatusType.Normal } }; QueryEvent query = new QueryEvent(); RegistrationEvent evt = new RegistrationEvent(); query.Add(evt, "SUBJ", HealthServiceRecordSiteRoleType.SubjectOf, null); evt.Status = StatusType.Active | StatusType.Obsolete; evt.Add(subject, "SUBJ", HealthServiceRecordSiteRoleType.SubjectOf, null); query.Add(new QueryParameters() { MatchingAlgorithm = MatchAlgorithm.Exact, MatchStrength = MatchStrength.Exact }, "FLTR", HealthServiceRecordSiteRoleType.FilterOf, null); var tRecordIds = QueryRecord(query); if (tRecordIds.Length != 1) { throw new MissingPrimaryKeyException(ApplicationContext.LocaleService.GetString("DBCF004")); } else if (tRecordIds[0].Domain != configService.OidRegistrar.GetOid(ClientRegistryOids.REGISTRATION_EVENT).Oid) { throw new MissingPrimaryKeyException(ApplicationContext.LocaleService.GetString("DBCF005")); } tryDec = Decimal.Parse(tRecordIds[0].Identifier); hsrEvent.AlternateIdentifier = tRecordIds[0]; }
/// <summary> /// De-persist the component from the database /// </summary> public System.ComponentModel.IComponent DePersist(System.Data.IDbConnection conn, decimal identifier, IContainer container, HealthServiceRecordSiteRoleType?roleType, bool loadFast) { Place retVal = new Place(); IDbCommand cmd = DbUtil.CreateCommandStoredProc(conn, null); try { // Determine the container RegistrationEvent hsrParent = container as RegistrationEvent; retVal = GetLocation(conn, null, new DomainIdentifier() { Identifier = identifier.ToString() }); // Data reader if (hsrParent != null) { hsrParent.Add(retVal, Guid.NewGuid().ToString(), roleType.Value, retVal.AlternateIdentifiers); } else { container.Add(retVal); } } finally { cmd.Dispose(); } return(retVal); }
/// <summary> /// Find conflicts using identifiers /// </summary> public IEnumerable <SVC.Core.DataTypes.VersionedDomainIdentifier> FindIdConflicts(RegistrationEvent registration) { var registrationService = this.Context.GetService(typeof(IDataRegistrationService)) as IDataRegistrationService; VersionedDomainIdentifier[] pid = null; // Check if the person exists just via the identifier? // This is important because it is not necessarily a merge but an "update if exists" var subject = registration.FindComponent(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf) as Person; QueryParameters qp = new QueryParameters() { Confidence = 1.0f, MatchingAlgorithm = MatchAlgorithm.Exact, MatchStrength = MatchStrength.Exact }; var config = this.Context.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService; List <VersionedDomainIdentifier> conflictIds = new List <VersionedDomainIdentifier>(); foreach (var id in subject.AlternateIdentifiers) { var data = config.OidRegistrar.FindData(id.Domain); if (data == null || !data.Attributes.Exists(a => a.Key == "IsUniqueIdentifier" && Convert.ToBoolean(a.Value))) { continue; } 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); registrationEvent.Add(new Person() { AlternateIdentifiers = new List <DomainIdentifier>() { id } }, "SUBJ", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf, null); // Perform the query pid = registrationService.QueryRecord(patientQuery); foreach (var result in pid) { if (!conflictIds.Exists(o => o.Identifier.ToString() == result.Identifier)) { conflictIds.Add(result); } } } return(conflictIds.ToArray()); }
/// <summary> /// Merge two items together /// </summary> public void Resolve(IEnumerable <SVC.Core.DataTypes.VersionedDomainIdentifier> victimIds, SVC.Core.DataTypes.VersionedDomainIdentifier survivorId, SVC.Core.Services.DataPersistenceMode mode) { // First, we load the survivor if (survivorId.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), "survivorId"); } // Load the survivor var persistenceService = this.Context.GetService(typeof(IDataPersistenceService)) as IDataPersistenceService; var survivorRegistrationEvent = persistenceService.GetContainer(survivorId, true) as RegistrationEvent; if (survivorRegistrationEvent == null) { throw new InvalidOperationException("Could not load target registration event"); } var survivorPerson = survivorRegistrationEvent.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf) as Person; if (survivorPerson == null) { throw new InvalidOperationException("Target registration event has no SubjectOf relationship of type Person"); } // Create the merge registration event RegistrationEvent mergeRegistrationEvent = new RegistrationEvent() { Mode = RegistrationEventType.Replace, EventClassifier = RegistrationEventType.Register, LanguageCode = survivorRegistrationEvent.LanguageCode }; mergeRegistrationEvent.EventType = new CodeValue("ADMIN_MRG"); mergeRegistrationEvent.Add(new ChangeSummary() { ChangeType = new CodeValue("ADMIN_MRG"), EffectiveTime = new TimestampSet() { Parts = new List <TimestampPart>() { new TimestampPart(TimestampPart.TimestampPartType.Standlone, DateTime.Now, "F") } }, Timestamp = DateTime.Now, LanguageCode = survivorRegistrationEvent.LanguageCode }, "CHG", HealthServiceRecordSiteRoleType.ReasonFor | HealthServiceRecordSiteRoleType.OlderVersionOf, null); survivorPerson.Site = null; mergeRegistrationEvent.Add(survivorPerson, "SUBJ", HealthServiceRecordSiteRoleType.SubjectOf, null); // Next, we do a replaces relationship for each of the victims (loading them as well since the ID is of the patient not reg event) foreach (var id in victimIds) { var replacementReg = persistenceService.GetContainer(id, true) as RegistrationEvent; if (replacementReg == null) { throw new InvalidOperationException("Could not load victim registration event"); } var replacementPerson = replacementReg.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf) as Person; if (replacementPerson == null) { throw new InvalidOperationException("Victim registration event has no SubjectOf relationship of type Person"); } // Now, create replaces survivorPerson.Add(new PersonRegistrationRef() { AlternateIdentifiers = new List <DomainIdentifier>() { new DomainIdentifier() { Domain = ApplicationContext.ConfigurationService.OidRegistrar.GetOid(ClientRegistryOids.CLIENT_CRID).Oid, Identifier = replacementPerson.Id.ToString() } } }, Guid.NewGuid().ToString(), HealthServiceRecordSiteRoleType.ReplacementOf, null); } // Now persist the replacement IDbConnection conn = DatabasePersistenceService.ConnectionManager.GetConnection(); IDbTransaction tx = null; try { // Update container var vid = persistenceService.UpdateContainer(mergeRegistrationEvent, DataPersistenceMode.Production); tx = conn.BeginTransaction(); foreach (var id in victimIds) { using (IDbCommand cmd = DbUtil.CreateCommandStoredProc(conn, tx)) { cmd.CommandText = "mrg_cand"; cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "from_id_in", DbType.Decimal, Decimal.Parse(id.Identifier))); cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "to_id_in", DbType.Decimal, Decimal.Parse(survivorId.Identifier))); cmd.ExecuteNonQuery(); } // Obsolete the victim identifier merges using (IDbCommand cmd = DbUtil.CreateCommandStoredProc(conn, tx)) { cmd.CommandText = "obslt_mrg"; cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "from_id_in", DbType.Decimal, Decimal.Parse(id.Identifier))); cmd.ExecuteNonQuery(); } } tx.Commit(); } catch (Exception e) { if (tx != null) { tx.Rollback(); } throw; } finally { DatabasePersistenceService.ConnectionManager.ReleaseConnection(conn); } }
/// <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> /// Notify the operation /// </summary> public void Notify(NotificationQueueWorkItem workItem) { // configuration service ISystemConfigurationService config = this.Context.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService; ILocalizationService locale = this.Context.GetService(typeof(ILocalizationService)) as ILocalizationService; // Common message bits we need to update IMessage notificationMessage = null; MSH msh = null; PID pid = null; EVN evn = null; PV1 pv1 = null; MRG mrg = null; // Identify the work item action switch (workItem.Action) { case MARC.HI.EHRS.CR.Notification.PixPdq.Configuration.ActionType.Create: { ADT_A01 message = new ADT_A01(); msh = message.MSH; pid = message.PID; evn = message.EVN; pv1 = message.PV1; notificationMessage = message; msh.MessageType.TriggerEvent.Value = "A04"; break; } case MARC.HI.EHRS.CR.Notification.PixPdq.Configuration.ActionType.DuplicatesResolved: { ADT_A39 message = new ADT_A39(); msh = message.MSH; msh.MessageType.TriggerEvent.Value = "A40"; pid = message.GetPATIENT(0).PID; evn = message.EVN; pv1 = message.GetPATIENT(0).PV1; mrg = message.GetPATIENT(0).MRG; notificationMessage = message; break; }; case MARC.HI.EHRS.CR.Notification.PixPdq.Configuration.ActionType.Update: { ADT_A01 message = new ADT_A01(); msh = message.MSH; pid = message.PID; evn = message.EVN; pv1 = message.PV1; notificationMessage = message; msh.MessageType.TriggerEvent.Value = "A08"; break; } } // Populate the MSH header first this.UpdateMSH(msh, config); // Populate the EVN segment evn.EventTypeCode.Value = workItem.Event.Mode.ToString(); evn.RecordedDateTime.TimeOfAnEvent.Value = (TS)workItem.Event.Timestamp; // Populate the PID segment Person subject = workItem.Event.FindComponent(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf) as Person; this.UpdatePID(subject, pid, config); pv1.PatientClass.Value = "I"; // Populate MRG if (mrg != null) { var registration = this.Context.GetService(typeof(IDataRegistrationService)) as IDataRegistrationService; var persistence = this.Context.GetService(typeof(IDataPersistenceService)) as IDataPersistenceService; var replacements = subject.FindAllComponents(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.ReplacementOf); foreach (PersonRegistrationRef rplc in replacements) { // First, need to de-persist the identifiers QueryParameters qp = new QueryParameters() { Confidence = 1.0f, MatchingAlgorithm = MatchAlgorithm.Exact, MatchStrength = MatchStrength.Exact }; var queryEvent = new QueryEvent(); var patientQuery = new RegistrationEvent(); queryEvent.Add(qp, "FLT", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.FilterOf, null); patientQuery.Add(new Person() { AlternateIdentifiers = rplc.AlternateIdentifiers }, "SUBJ", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf, null); queryEvent.Add(patientQuery, "SUBJ", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf, null); // Perform the query var patientIdentifiers = registration.QueryRecord(queryEvent); if (patientIdentifiers.Length == 0) { throw new InvalidOperationException(); } var replacedPerson = (persistence.GetContainer(patientIdentifiers[0], true) as RegistrationEvent).FindComponent(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf) as Person; foreach (var ii in replacedPerson.AlternateIdentifiers.FindAll(o => this.Target.NotificationDomain.Exists(d => d.Domain == o.Domain))) { var cx = mrg.GetPriorPatientIdentifierList(mrg.PriorAlternatePatientIDRepetitionsUsed); cx.ID.Value = ii.Identifier; if (String.IsNullOrEmpty(ii.AssigningAuthority)) { cx.AssigningAuthority.NamespaceID.Value = config.OidRegistrar.FindData(ii.Domain).Attributes.Find(o => o.Key == "AssigningAuthorityName").Value; } else { cx.AssigningAuthority.NamespaceID.Value = ii.AssigningAuthority; } cx.AssigningAuthority.UniversalID.Value = ii.Domain; cx.AssigningAuthority.UniversalIDType.Value = "ISO"; } } } // Send var queueItem = new Hl7MessageQueue.MessageQueueWorkItem(this.Target, notificationMessage); if (!queueItem.TrySend()) { Trace.TraceWarning(locale.GetString("NTFW005")); Hl7MessageQueue.Current.EnqueueMessageItem(queueItem); } }
/// <summary> /// Get all registrations matching the query prototype /// </summary> public RegistrationEventCollection GetRegistrations(Person queryPrototype, int offset, int count) { // Get all Services IAuditorService auditSvc = ApplicationContext.CurrentContext.GetService(typeof(IAuditorService)) as IAuditorService; IDataRegistrationService regSvc = ApplicationContext.CurrentContext.GetService(typeof(IDataRegistrationService)) as IDataRegistrationService; IDataPersistenceService repSvc = ApplicationContext.CurrentContext.GetService(typeof(IDataPersistenceService)) as IDataPersistenceService; // Audit message AuditData audit = this.ConstructAuditData(ActionType.Read, EventIdentifierType.Export); audit.EventTypeCode = new CodeValue("ADM_GetRegistrations"); try { // Result identifiers VersionedDomainIdentifier[] vids = null; var dummyQuery = new QueryEvent(); var regEvt = new RegistrationEvent() { EventClassifier = RegistrationEventType.Register }; dummyQuery.Add(regEvt, "SUBJ", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf, null); if (queryPrototype == null) { regEvt.Add(new Person(), "SUBJ", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf, null); } else { regEvt.Add(queryPrototype, "SUBJ", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf, null); } vids = regSvc.QueryRecord(dummyQuery); RegistrationEventCollection retVal = new RegistrationEventCollection(); Object syncLock = new object(); retVal.Count = vids.Length; // Now fetch each one asynchronously using (WaitThreadPool thdPool = new WaitThreadPool(Environment.ProcessorCount * 2)) { foreach (var id in vids.Skip(offset).Take(count)) { thdPool.QueueUserWorkItem( delegate(object state) { try { var itm = repSvc.GetContainer(state as VersionedDomainIdentifier, true); lock (syncLock) retVal.Event.Add(itm as RegistrationEvent); } catch (Exception e) { Trace.TraceError("Could not fetch result {0} : {1}", (state as VersionedDomainIdentifier).Identifier, e.ToString()); } } , id); } // Wait until fetch is done thdPool.WaitOne(new TimeSpan(0, 0, 30), false); } //retVal.Event.Sort((a, b) => b.Timestamp.CompareTo(a.Timestamp)); // Add audit data foreach (var res in retVal.Event) { audit.AuditableObjects.Add(new AuditableObject() { IDTypeCode = AuditableObjectIdType.ReportNumber, LifecycleType = AuditableObjectLifecycle.Export, ObjectId = String.Format("{0}^^^&{1}&ISO", res.AlternateIdentifier.Identifier, res.AlternateIdentifier.Domain), Role = AuditableObjectRole.MasterFile, Type = AuditableObjectType.SystemObject, QueryData = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("loadFast=true")) }); } return(retVal); } catch (Exception e) { Trace.TraceError("Could not execute GetRegistrations : {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 a patient registry duplicates resolved message /// </summary> private Everest.Interfaces.IInteraction CreatePatientRegistryDuplicatesResolvedMessage(Core.ComponentModel.RegistrationEvent registrationEvent, TargetConfiguration configuration) { // Construct the return value PRPA_IN201304UV02 retVal = new PRPA_IN201304UV02( Guid.NewGuid(), DateTime.Now, PRPA_IN201304UV02.GetInteractionId(), ProcessingID.Production, "T", AcknowledgementCondition.Always); // Construct the sending node retVal.VersionCode = HL7StandardVersionCode.Version3_Prerelease1; retVal.Sender = CreateSenderNode(); retVal.Receiver.Add(CreateReceiverNode(configuration)); // Construct the control act process retVal.controlActProcess = new Everest.RMIM.UV.NE2008.MFMI_MT700701UV01.ControlActProcess <Everest.RMIM.UV.NE2008.PRPA_MT201303UV02.Patient, object>("EVN"); // Get the subjects and components var subject = registrationEvent.FindComponent(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf) as Person; var providerOrg = subject.FindComponent(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.PlaceOfEntry | SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.InformantTo) as HealthcareParticipant; var custodian = registrationEvent.FindComponent(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.PlaceOfRecord | SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.ResponsibleFor); var replacements = subject.FindAllComponents(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.ReplacementOf); // Create the person List <DomainIdentifier> identifiers = new List <DomainIdentifier>(subject.AlternateIdentifiers.FindAll(o => configuration.NotificationDomain.Exists(d => d.Domain.Equals(o.Domain)))); var eventRegistration = new Everest.RMIM.UV.NE2008.MFMI_MT700701UV01.Subject1 <Everest.RMIM.UV.NE2008.PRPA_MT201303UV02.Patient, object>(false, new Everest.RMIM.UV.NE2008.MFMI_MT700701UV01.RegistrationEvent <Everest.RMIM.UV.NE2008.PRPA_MT201303UV02.Patient, object>( ActStatus.Active, new Everest.RMIM.UV.NE2008.MFMI_MT700701UV01.Subject2 <Everest.RMIM.UV.NE2008.PRPA_MT201303UV02.Patient>( new Everest.RMIM.UV.NE2008.PRPA_MT201303UV02.Patient( CreateIISet(identifiers), RoleStatus.Active, CreatePerson(subject, new TargetConfiguration(String.Empty, null, configuration.Notifier.GetType().Name, null)), providerOrg == null ? null : CreateProviderOrganization(providerOrg) ) ) ) ); // Get person data var personData = eventRegistration.RegistrationEvent.Subject1.registeredRole.PatientEntityChoiceSubject as Everest.RMIM.UV.NE2008.PRPA_MT201303UV02.Person; personData.AsOtherIDs.Clear(); retVal.controlActProcess.Subject.Add(eventRegistration); // TODO: Replacement of compatibility mode for other XDS registries // Replacement var registration = this.Context.GetService(typeof(IDataRegistrationService)) as IDataRegistrationService; var persistence = this.Context.GetService(typeof(IDataPersistenceService)) as IDataPersistenceService; foreach (PersonRegistrationRef rplc in replacements) { // First, need to de-persist the identifiers QueryParameters qp = new QueryParameters() { Confidence = 1.0f, MatchingAlgorithm = MatchAlgorithm.Exact, MatchStrength = MatchStrength.Exact }; var patientQuery = new QueryEvent(); RegistrationEvent regEvent = new RegistrationEvent() { }; patientQuery.Add(regEvent, "SUBJ", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf, null); patientQuery.Add(qp, "FLT", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.FilterOf, null); regEvent.Add(new Person() { AlternateIdentifiers = rplc.AlternateIdentifiers }, "SUBJ", SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf, null); // Perform the query var pid = registration.QueryRecord(patientQuery); if (pid.Length == 0) { throw new InvalidOperationException(); } var replacedPerson = (persistence.GetContainer(pid[0], true) as RegistrationEvent).FindComponent(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf) as Person; var ids = CreateIISet(replacedPerson.AlternateIdentifiers.FindAll(o => configuration.NotificationDomain.Exists(d => d.Domain == o.Domain))); if (ids.Count == 0) { ; // TODO: Trace log } else { eventRegistration.RegistrationEvent.ReplacementOf.Add(new Everest.RMIM.UV.NE2008.MFMI_MT700701UV01.ReplacementOf( new Everest.RMIM.UV.NE2008.MFMI_MT700701UV01.PriorRegistration( null, ActStatus.Obsolete, new Everest.RMIM.UV.NE2008.MFMI_MT700701UV01.Subject3( new Everest.RMIM.UV.NE2008.MFMI_MT700701UV01.PriorRegisteredRole( ids ) ), null ) )); } } if (eventRegistration.RegistrationEvent.ReplacementOf.Count == 0) { throw new InvalidOperationException("Nothing to do"); } // Custodian? eventRegistration.RegistrationEvent.Custodian = CreateCustodian(registrationEvent, configuration); return(retVal); }
/// <summary> /// De-persist the specified data /// </summary> public System.ComponentModel.IComponent DePersist(IDbConnection conn, decimal identifier, IContainer container, HealthServiceRecordSiteRoleType?roleType, bool loadFast) { // TODO: Ensure that when a parent with context conduction exists, to grab contextual data (authors, etc...) from the parent RegistrationEvent retVal = new RegistrationEvent(); // Configuration service ISystemConfigurationService configService = ApplicationContext.ConfigurationService; //ApplicationContext.Current.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService; // Get the health service event IDbCommand cmd = DbUtil.CreateCommandStoredProc(conn, null); try { cmd.CommandText = "get_hsr_crnt_vrsn"; cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "hsr_id_in", DbType.Decimal, identifier)); decimal tsId = default(decimal), cdId = default(decimal), rplcVersionId = default(decimal); // Read data IDataReader reader = cmd.ExecuteReader(); try { if (!reader.Read()) { return(null); } retVal.Id = Convert.ToDecimal(reader["hsr_id"]); retVal.VersionIdentifier = Convert.ToDecimal(reader["hsr_vrsn_id"]); retVal.AlternateIdentifier = new VersionedDomainIdentifier() { Domain = configService.OidRegistrar.GetOid(ClientRegistryOids.REGISTRATION_EVENT).Oid, Identifier = retVal.Id.ToString(), Version = retVal.VersionIdentifier.ToString(), }; retVal.EventClassifier = (RegistrationEventType)Convert.ToDecimal(reader["hsr_cls"]); retVal.Refuted = Convert.ToBoolean(reader["refuted_ind"]); retVal.Timestamp = DateTime.Parse(Convert.ToString(reader["aut_utc"])); retVal.Status = (StatusType)Convert.ToDecimal(reader["status_cs_id"]); tsId = Convert.ToDecimal(reader["efft_ts_set_id"]); cdId = Convert.ToDecimal(reader["evt_typ_cd_id"]); rplcVersionId = reader["rplc_vrsn_id"] == DBNull.Value ? default(decimal) : Convert.ToDecimal(reader["rplc_vrsn_id"]); retVal.LanguageCode = Convert.ToString(reader["lang_cs"]); } finally { reader.Close(); } // Read codes and times retVal.EventType = DbUtil.GetCodedValue(conn, null, cdId); retVal.EffectiveTime = DbUtil.GetEffectiveTimestampSet(conn, null, tsId); if (container != null) { container.Add(retVal); } // De-persist older versions if (!loadFast && rplcVersionId != default(decimal)) { var oldVersion = DePersist(conn, identifier, rplcVersionId, retVal, HealthServiceRecordSiteRoleType.OlderVersionOf, loadFast); if (oldVersion != null) { (oldVersion.Site as HealthServiceRecordSite).SiteRoleType = HealthServiceRecordSiteRoleType.OlderVersionOf; retVal.Add(oldVersion); } } if (roleType.HasValue && (roleType.Value & HealthServiceRecordSiteRoleType.ReplacementOf) == HealthServiceRecordSiteRoleType.ReplacementOf) { ; } else { DbUtil.DePersistComponents(conn, retVal, this, loadFast); } } finally { cmd.Dispose(); } return(retVal); }
/// <summary> /// Update container /// </summary> public VersionedDomainIdentifier UpdateContainer(System.ComponentModel.IContainer storageData, DataPersistenceMode mode) { if (m_disposed) { throw new ObjectDisposedException("DatabasePersistenceService"); } if (storageData == null) { throw new ArgumentNullException("storageData"); } // Get the persister IComponentPersister persister = GetPersister(storageData.GetType()); ISystemConfigurationService configService = ApplicationContext.ConfigurationService; if (persister != null) { IDbTransaction tx = null; IDbConnection conn = null; try { conn = DatabasePersistenceService.ConnectionManager.GetConnection(); RegistrationEvent hsrEvent = storageData as RegistrationEvent; // Is the record something that we have access to? if (hsrEvent.AlternateIdentifier != null && !hsrEvent.AlternateIdentifier.Domain.Equals(configService.OidRegistrar.GetOid(ClientRegistryOids.REGISTRATION_EVENT).Oid)) { throw new ArgumentException(String.Format("The record OID '{0}' cannot be retrieved by this repository, expecting OID '{1}'", hsrEvent.AlternateIdentifier.Domain, configService.OidRegistrar.GetOid(ClientRegistryOids.REGISTRATION_EVENT).Oid)); } decimal tryDec = default(decimal); bool isDirectUpdate = false; // Is there no event identifier ? if (hsrEvent.AlternateIdentifier != null && !Decimal.TryParse(hsrEvent.AlternateIdentifier.Identifier, out tryDec)) { throw new ArgumentException(String.Format("The identifier '{0}' is not a valid identifier for this repository", hsrEvent.AlternateIdentifier.Identifier)); } else if (hsrEvent.AlternateIdentifier == null) // The alternate identifier is null ... so we need to look up the registration event to version ... interesting.... { this.EnrichRegistrationEvent(conn, tx, hsrEvent); } else { // Get the person name isDirectUpdate = true; // Explicit update } // Validate and duplicate the components that are to be loaded as part of the new version var oldHsrEvent = GetContainer(hsrEvent.AlternateIdentifier, true) as RegistrationEvent; // Get the old container if (oldHsrEvent == null) { throw new MissingPrimaryKeyException(String.Format("Record {1}^^^&{0}&ISO does not exist", hsrEvent.AlternateIdentifier.Domain, hsrEvent.AlternateIdentifier.Identifier)); } PersonPersister cp = new PersonPersister(); // Validate the old record target Person oldRecordTarget = oldHsrEvent.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf) as Person, newRecordTarget = hsrEvent.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf) as Person; Person verifyRecordTarget = null; if (!isDirectUpdate) { Trace.TraceInformation("Not Direct update, enriching the Patient Data"); int idCheck = 0; while (verifyRecordTarget == null || idCheck > newRecordTarget.AlternateIdentifiers.Count) { verifyRecordTarget = cp.GetPerson(conn, null, newRecordTarget.AlternateIdentifiers[idCheck++], true); } if (verifyRecordTarget == null || oldRecordTarget.Id != verifyRecordTarget.Id) { throw new ConstraintException("The update request specifies a different subject than the request currently stored"); } } else { verifyRecordTarget = oldRecordTarget; } //newRecordTarget.VersionId = verifyRecordTarget.VersionId; newRecordTarget.Id = verifyRecordTarget.Id; // VAlidate classific if (oldHsrEvent.EventClassifier != hsrEvent.EventClassifier && (hsrEvent.EventClassifier & RegistrationEventType.Register) == RegistrationEventType.None) { throw new ConstraintException("Record type mis-match between update data and the data already in the persistence store"); } if (oldHsrEvent.LanguageCode != hsrEvent.LanguageCode) { throw new ConstraintException("Record language mis-match between update data and data already in persistence store. To change the language use a \"replaces\" relationship rather than an update"); } // Are we performing a component update? If so the only two components we want freshened are the CommentOn (for adding comments) // and the ReasonFor | OlderVersionOf comment for change summaries if ((hsrEvent.EventClassifier & RegistrationEventType.ComponentEvent) != 0) { for (int i = hsrEvent.XmlComponents.Count - 1; i >= 0; i--) { if (((hsrEvent.XmlComponents[i].Site as HealthServiceRecordSite).SiteRoleType & (HealthServiceRecordSiteRoleType.CommentOn | HealthServiceRecordSiteRoleType.ReasonFor | HealthServiceRecordSiteRoleType.OlderVersionOf | HealthServiceRecordSiteRoleType.TargetOf)) == 0) { hsrEvent.Remove(hsrEvent.XmlComponents[i]); } } } // Copy over any components that aren't already specified or updated in the new record // Merge the old and new. Sets the update mode appropriately cp.MergePersons(newRecordTarget, oldRecordTarget); // Next we copy this as a replacement of hsrEvent.RemoveAllFromRole(HealthServiceRecordSiteRoleType.SubjectOf); hsrEvent.Add(newRecordTarget, "SUBJ", HealthServiceRecordSiteRoleType.SubjectOf, null); // Begin and update tx = conn.BeginTransaction(); var retVal = persister.Persist(conn, tx, storageData as IComponent, true); if (m_configuration.OverridePersistenceMode.HasValue) { mode = m_configuration.OverridePersistenceMode.Value; } if (mode == DataPersistenceMode.Production) { tx.Commit(); tx = null; // Mark conflicts if any are outstanding pointing at the old versionj if (this.m_clientRegistryMerge != null) { var existingConflicts = this.m_clientRegistryMerge.GetConflicts(oldHsrEvent.AlternateIdentifier); if (existingConflicts.Count() > 0) { Trace.TraceInformation("Obsoleting existing conlflicts resolved"); this.m_clientRegistryMerge.ObsoleteConflicts(oldHsrEvent.AlternateIdentifier); } this.m_threadPool.QueueUserWorkItem(this.MarkConflictsAsync, retVal); } // Notify register if (this.m_notificationService != null) { if (hsrEvent.Mode == RegistrationEventType.Replace) { this.m_notificationService.NotifyDuplicatesResolved(hsrEvent); } else { this.m_notificationService.NotifyUpdate(hsrEvent); } } } else { tx.Rollback(); } return(retVal); } catch (Exception e) { if (tx != null) { tx.Rollback(); } throw; } finally { if (tx != null) { tx.Dispose(); } if (conn != null) { DatabasePersistenceService.ConnectionManager.ReleaseConnection(conn); conn = null; } } } else { Trace.TraceError("Can't find a persistence handler for type '{0}'", storageData.GetType().Name); throw new DataException(String.Format("Can't persist type '{0}'", storageData.GetType().Name)); } }
/// <summary> /// De-persist /// </summary> public System.ComponentModel.IComponent DePersist(System.Data.IDbConnection conn, decimal identifier, IContainer container, HealthServiceRecordSiteRoleType?roleType, bool loadFast) { HealthcareParticipant retVal = new HealthcareParticipant(); IDbCommand cmd = DbUtil.CreateCommandStoredProc(conn, null); try { // Determine the container RegistrationEvent hsrParent = container as RegistrationEvent; retVal = GetProvider(conn, null, new DomainIdentifier() { Identifier = identifier.ToString() }); // Data reader if (hsrParent != null) { cmd.CommandText = "get_hsr_ptcpt"; cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "hsr_id_in", DbType.Decimal, hsrParent.Id)); //cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "vrsn_id_in", DbType.Decimal, hsrParent.VersionIdentifier)); cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "ptcpt_id_in", DbType.Decimal, identifier)); cmd.Parameters.Add(DbUtil.CreateParameterIn(cmd, "ptcpt_cls_in", DbType.Decimal, (decimal)roleType.Value)); // Role Decimal repOrgId = default(decimal); List <DomainIdentifier> originalIds = new List <DomainIdentifier>(); // Execute a reader IDataReader reader = cmd.ExecuteReader(); try { // Read a row while (reader.Read()) { repOrgId = reader["ptcpt_rep_org_id"] == DBNull.Value ? default(decimal) : Convert.ToDecimal(reader["ptcpt_rep_org_id"]); originalIds.Add(new DomainIdentifier() { Domain = Convert.ToString(reader["orig_id_domain"]), Identifier = Convert.ToString(reader["orig_id"]), IsLicenseAuthority = Convert.ToBoolean(reader["license_ind"]) }); } } finally { reader.Close(); } // Representative of if (repOrgId != default(decimal)) { retVal.Add(GetProvider(conn, null, new DomainIdentifier() { Identifier = repOrgId.ToString() }), "REPOF", HealthServiceRecordSiteRoleType.RepresentitiveOf, null); } // Add to parent hsrParent.Add(retVal, Guid.NewGuid().ToString(), roleType.Value, originalIds); } else if (container != null) { container.Add(retVal); } } finally { cmd.Dispose(); } // TODO: Get original identifiers for this link return(retVal); }