예제 #1
0
        /// <summary>
        /// Map the specified <paramref name="modelInstance"/> to a bundle
        /// </summary>
        public Resource MapToFhir(IdentifiedData modelInstance)
        {
            if (!(modelInstance is Core.Model.Collection.Bundle sdbBundle))
            {
                this.m_tracer.TraceError("Instance must be a bundle");
                throw new ArgumentException(nameof(modelInstance), this.m_localizationService.FormatString("error.type.ArgumentException", new
                {
                    param = "bundle"
                }));
            }

            var retVal = new Hl7.Fhir.Model.Bundle()
            {
                Type = Hl7.Fhir.Model.Bundle.BundleType.TransactionResponse,
                Meta = new Meta()
                {
                    LastUpdated = DateTimeOffset.Now
                }
            };

            // Parse return value
            foreach (var entry in sdbBundle.Item)
            {
                var handler = FhirResourceHandlerUtil.GetMapperForInstance(entry);
                if (handler == null)
                {
                    continue;                  // TODO: Warn
                }
                retVal.Entry.Add(new Hl7.Fhir.Model.Bundle.EntryComponent()
                {
                    Resource = handler.MapToFhir(entry)
                });
            }
            return(retVal);
        }
예제 #2
0
        /// <summary>
        /// Map the bundle to a model transaction
        /// </summary>
        public IdentifiedData MapToModel(Resource resourceInstance)
        {
            // Resource instance validation and convert
            if (!(resourceInstance is Hl7.Fhir.Model.Bundle fhirBundle))
            {
                this.m_tracer.TraceError("Argument must be a bundle");
                throw new ArgumentException(this.m_localizationService.FormatString("error.type.ArgumentException", new
                {
                    param = "bundle"
                }));
            }

            var sdbBundle = new Core.Model.Collection.Bundle();

            foreach (var entry in fhirBundle.Entry)
            {
                if (!entry.Resource.TryDeriveResourceType(out ResourceType entryType))
                {
                    continue;
                }
                var handler = FhirResourceHandlerUtil.GetResourceHandler(entryType) as IFhirResourceMapper;

                // Allow this entry to know its context in the bundle
                entry.Resource.AddAnnotation(fhirBundle);
                entry.Resource.AddAnnotation(sdbBundle);

                // Map and add to bundle
                var itm = handler.MapToModel(entry.Resource);
                sdbBundle.Remove(itm.Key.GetValueOrDefault());
                sdbBundle.Add(itm);

                // HACK: If the ITM is a relationship or participation insert it into the bundle
                if (itm is ITargetedAssociation targetedAssociation && targetedAssociation.TargetEntity != null)
                {
                    sdbBundle.Insert(sdbBundle.Item.Count - 1, targetedAssociation.TargetEntity as IdentifiedData);
                    itm = targetedAssociation.TargetEntity as IdentifiedData;
                }

                // Add original URLs so that subsequent bundle entries (which might reference this entry) can resolve
                if (itm is ITaggable taggable)
                {
                    taggable.AddTag(FhirConstants.OriginalUrlTag, entry.FullUrl);
                    taggable.AddTag(FhirConstants.OriginalIdTag, entry.Resource.Id);
                }

                if (entry.Request != null)
                {
                    sdbBundle.FocalObjects.Add(itm.Key.Value);
                }
            }

            sdbBundle.Item.RemoveAll(o => o == null || o is ITaggable taggable && taggable.GetTag(FhirConstants.PlaceholderTag) == "true");
            return(sdbBundle);
        }
예제 #3
0
        /// <summary>
        /// Get included objects
        /// </summary>
        protected override IEnumerable <Resource> GetReverseIncludes(Core.Model.Roles.Patient resource, IEnumerable <IncludeInstruction> reverseIncludePaths)
        {
            return(reverseIncludePaths.SelectMany(includeInstruction =>
            {
                switch (includeInstruction.Type)
                {
                case ResourceType.RelatedPerson:
                    // Load all related persons and convert them
                    var rpHandler = FhirResourceHandlerUtil.GetMappersFor(ResourceType.RelatedPerson).FirstOrDefault();
                    return resource.LoadCollection(o => o.Relationships)
                    .Where(o => o.ClassificationKey != RelationshipClassKeys.ContainedObjectLink &&
                           o.RelationshipRoleKey == null &&
                           this.GetRelatedPersonConceptUuids().Contains(o.RelationshipTypeKey.Value))
                    .Select(o => rpHandler.MapToFhir(o));

                default:
                    this.m_tracer.TraceError($"{includeInstruction.Type} is not supported reverse include");
                    throw new InvalidOperationException(this.m_localizationService.GetString("error.type.NotSupportedException.userMessage"));
                }
            }));
        }
예제 #4
0
        /// <summary>
        /// Map FHIR resource to our bundle
        /// </summary>
        protected override Core.Model.Collection.Bundle MapToModel(SanteDB.Messaging.FHIR.Resources.Bundle resource, RestOperationContext webOperationContext)
        {
            var retVal = new Core.Model.Collection.Bundle();

            foreach (var entry in resource.Entry)
            {
                var entryType = entry.Resource.Resource?.GetType();
                if (entryType == null)
                {
                    continue;
                }
                var handler = FhirResourceHandlerUtil.GetResourceHandler(entryType.GetCustomAttribute <XmlRootAttribute>().ElementName) as IBundleResourceHandler;
                if (handler == null)
                {
                    this.traceSource.TraceWarning("Can't find bundle handler for {0}...", entryType.Name);
                    continue;
                }
                retVal.Add(handler.MapToModel(entry, webOperationContext, resource));
            }
            retVal.Item.RemoveAll(o => o == null);
            return(retVal);
        }
예제 #5
0
        /// <summary>
        /// Map a patient object to FHIR.
        /// </summary>
        /// <param name="model">The patient to map to FHIR</param>
        /// <returns>Returns the mapped FHIR resource.</returns>
        protected override Patient MapToFhir(Core.Model.Roles.Patient model)
        {
            // If the model is being constructed as part of a bundle, then the caller
            // should have included the bundle so we can add any related resources
            var partOfBundle = model.GetAnnotations <Bundle>().FirstOrDefault();

            var retVal = DataTypeConverter.CreateResource <Patient>(model);

            retVal.Active  = StatusKeys.ActiveStates.Contains(model.StatusConceptKey.Value);
            retVal.Address = model.GetAddresses().Select(DataTypeConverter.ToFhirAddress).ToList();

            if (model.DateOfBirth.HasValue)
            {
                switch (model.DateOfBirthPrecision.GetValueOrDefault())
                {
                case DatePrecision.Day:
                    retVal.BirthDate = model.DateOfBirth.Value.ToString("yyyy-MM-dd");
                    break;

                case DatePrecision.Month:
                    retVal.BirthDate = model.DateOfBirth.Value.ToString("yyyy-MM");
                    break;

                case DatePrecision.Year:
                    retVal.BirthDate = model.DateOfBirth.Value.ToString("yyyy");
                    break;
                }
            }

            // Deceased precision
            if (model.DeceasedDate.HasValue)
            {
                if (model.DeceasedDate == DateTime.MinValue)
                {
                    retVal.Deceased = new FhirBoolean(true);
                }
                else
                {
                    switch (model.DeceasedDatePrecision)
                    {
                    case DatePrecision.Day:
                        retVal.Deceased = new FhirDateTime(model.DeceasedDate.Value.Year, model.DeceasedDate.Value.Month, model.DeceasedDate.Value.Day);
                        break;

                    case DatePrecision.Month:
                        retVal.Deceased = new FhirDateTime(model.DeceasedDate.Value.Year, model.DeceasedDate.Value.Month);
                        break;

                    case DatePrecision.Year:
                        retVal.Deceased = new FhirDateTime(model.DeceasedDate.Value.Year);
                        break;

                    default:
                        retVal.Deceased = DataTypeConverter.ToFhirDateTime(model.DeceasedDate);
                        break;
                    }
                }
            }

            if (model.GenderConceptKey.HasValue)
            {
                retVal.Gender = DataTypeConverter.ToFhirEnumeration <AdministrativeGender>(model.GenderConceptKey, "http://hl7.org/fhir/administrative-gender", true);
            }

            retVal.Identifier    = model.Identifiers?.Select(DataTypeConverter.ToFhirIdentifier).ToList();
            retVal.MultipleBirth = model.MultipleBirthOrder == 0 ? (DataType) new FhirBoolean(true) : model.MultipleBirthOrder.HasValue ? new Integer(model.MultipleBirthOrder.Value) : null;
            retVal.Name          = model.GetNames().Select(DataTypeConverter.ToFhirHumanName).ToList();
            retVal.Telecom       = model.GetTelecoms().Select(DataTypeConverter.ToFhirTelecom).ToList();
            retVal.Communication = model.GetPersonLanguages().Select(DataTypeConverter.ToFhirCommunicationComponent).ToList();

            foreach (var rel in model.GetRelationships().Where(o => !o.InversionIndicator))
            {
                if (rel.RelationshipTypeKey == EntityRelationshipTypeKeys.Contact)
                {
                    var relEntity = rel.LoadProperty(o => o.TargetEntity);

                    if (relEntity is Core.Model.Entities.Person person)
                    {
                        var contact = new Patient.ContactComponent()
                        {
                            ElementId    = $"{person.Key}",
                            Address      = DataTypeConverter.ToFhirAddress(person.GetAddresses().FirstOrDefault()),
                            Relationship = new List <CodeableConcept>()
                            {
                                DataTypeConverter.ToFhirCodeableConcept(rel.RelationshipRoleKey, "http://terminology.hl7.org/CodeSystem/v2-0131"),
                                DataTypeConverter.ToFhirCodeableConcept(rel.RelationshipTypeKey, "http://terminology.hl7.org/CodeSystem/v2-0131")
                            }.OfType <CodeableConcept>().ToList(),
                            Name = DataTypeConverter.ToFhirHumanName(person.GetNames().FirstOrDefault()),
                            // TODO: Gender
                            Gender  = DataTypeConverter.ToFhirEnumeration <AdministrativeGender>(person.GenderConceptKey, "http://hl7.org/fhir/administrative-gender"),
                            Telecom = person.GetTelecoms().Select(t => DataTypeConverter.ToFhirTelecom(t)).ToList()
                        };

                        var scoper = person.LoadCollection(o => o.Relationships).FirstOrDefault(o => o.RelationshipTypeKey == EntityRelationshipTypeKeys.Scoper);
                        if (scoper != null)
                        {
                            contact.Organization = DataTypeConverter.CreateNonVersionedReference <Hl7.Fhir.Model.Organization>(scoper.LoadProperty(o => o.TargetEntity));
                        }
                        DataTypeConverter.AddExtensions(person, contact);
                        retVal.Contact.Add(contact);
                    }
                    else if (relEntity is Core.Model.Entities.Organization org) // it *IS* an organization
                    {
                        var contact = new Patient.ContactComponent()
                        {
                            ElementId    = $"{org.Key}",
                            Relationship = new List <CodeableConcept>()
                            {
                                DataTypeConverter.ToFhirCodeableConcept(rel.RelationshipRoleKey, "http://terminology.hl7.org/CodeSystem/v2-0131"),
                                DataTypeConverter.ToFhirCodeableConcept(rel.RelationshipTypeKey, "http://terminology.hl7.org/CodeSystem/v2-0131")
                            }.OfType <CodeableConcept>().ToList(),
                            Organization = DataTypeConverter.CreateNonVersionedReference <Hl7.Fhir.Model.Organization>(org)
                        };
                        retVal.Contact.Add(contact);
                    }
                }
                else if (rel.RelationshipTypeKey == EntityRelationshipTypeKeys.Scoper)
                {
                    var scoper = rel.LoadProperty(o => o.TargetEntity);

                    retVal.ManagingOrganization = DataTypeConverter.CreateNonVersionedReference <Hl7.Fhir.Model.Organization>(scoper);

                    // If this is part of a bundle, include it
                    partOfBundle?.Entry.Add(new Bundle.EntryComponent
                    {
                        FullUrl  = $"{MessageUtil.GetBaseUri()}/Organization/{scoper.Key}",
                        Resource = FhirResourceHandlerUtil.GetMapperForInstance(scoper).MapToFhir(scoper)
                    });
                }
                else if (rel.RelationshipTypeKey == EntityRelationshipTypeKeys.HealthcareProvider)
                {
                    var practitioner = rel.LoadProperty(o => o.TargetEntity);

                    retVal.GeneralPractitioner.Add(DataTypeConverter.CreateVersionedReference <Practitioner>(practitioner));

                    // If this is part of a bundle, include it
                    partOfBundle?.Entry.Add(new Bundle.EntryComponent
                    {
                        FullUrl  = $"{MessageUtil.GetBaseUri()}/Practitioner/{practitioner.Key}",
                        Resource = FhirResourceHandlerUtil.GetMapperForInstance(practitioner).MapToFhir(practitioner)
                    });
                }
                else if (rel.RelationshipTypeKey == EntityRelationshipTypeKeys.Replaces)
                {
                    retVal.Link.Add(this.CreateLink <Patient>(rel.TargetEntityKey.Value, Patient.LinkType.Replaces));
                }
                else if (rel.RelationshipTypeKey == EntityRelationshipTypeKeys.Duplicate)
                {
                    retVal.Link.Add(this.CreateLink <Patient>(rel.TargetEntityKey.Value, Patient.LinkType.Seealso));
                }
                else if (rel.RelationshipTypeKey == MDM_MASTER_LINK) // HACK: MDM Master Record
                {
                    if (rel.SourceEntityKey.HasValue && rel.SourceEntityKey != model.Key)
                    {
                        retVal.Link.Add(this.CreateLink <Patient>(rel.SourceEntityKey.Value, Patient.LinkType.Seealso));
                    }
                    else // Is a local
                    {
                        retVal.Link.Add(this.CreateLink <Patient>(rel.TargetEntityKey.Value, Patient.LinkType.Refer));
                    }
                }
                else if (rel.ClassificationKey == EntityRelationshipTypeKeys.EquivalentEntity)
                {
                    retVal.Link.Add(this.CreateLink <Patient>(rel.TargetEntityKey.Value, Patient.LinkType.Refer));
                }
                else if (partOfBundle != null) // This is part of a bundle and we need to include it
                {
                    // HACK: This piece of code is used to add any RelatedPersons to the container bundle if it is part of a bundle
                    if (this.GetRelatedPersonConceptUuids().Contains(rel.RelationshipTypeKey.Value))
                    {
                        var relative = FhirResourceHandlerUtil.GetMapperForInstance(rel).MapToFhir(rel);
                        partOfBundle.Entry.Add(new Bundle.EntryComponent()
                        {
                            FullUrl  = $"{MessageUtil.GetBaseUri()}/RelatedPerson/{rel.Key}",
                            Resource = relative
                        });
                    }
                }
            }

            // Reverse relationships of family member?
            var uuids = model.Relationships.Where(r => r.RelationshipTypeKey == MDM_MASTER_LINK).Select(r => r.SourceEntityKey).Union(new Guid?[] { model.Key }).ToArray();
            var familyMemberConcepts = this.GetFamilyMemberUuids();
            var reverseRelationships = this.m_erRepository.Find(o => uuids.Contains(o.TargetEntityKey) && familyMemberConcepts.Contains(o.RelationshipTypeKey.Value) && o.ObsoleteVersionSequenceId == null);

            foreach (var rrv in reverseRelationships)
            {
                retVal.Link.Add(new Patient.LinkComponent
                {
                    Type  = Patient.LinkType.Seealso,
                    Other = DataTypeConverter.CreateNonVersionedReference <RelatedPerson>(rrv)
                });

                // If this is part of a bundle, include it
                partOfBundle?.Entry.Add(new Bundle.EntryComponent
                {
                    FullUrl  = $"{MessageUtil.GetBaseUri()}/RelatedPerson/{rrv.Key}",
                    Resource = FhirResourceHandlerUtil.GetMappersFor(ResourceType.RelatedPerson).First().MapToFhir(rrv)
                });
            }

            // Was this record replaced?
            if (!retVal.Active.GetValueOrDefault())
            {
                var replacedRelationships = this.m_erRepository.Find(o => uuids.Contains(o.TargetEntityKey) && o.RelationshipTypeKey == EntityRelationshipTypeKeys.Replaces && o.ObsoleteVersionSequenceId == null);

                foreach (var repl in replacedRelationships)
                {
                    retVal.Link.Add(new Patient.LinkComponent()
                    {
                        Type  = Patient.LinkType.ReplacedBy,
                        Other = DataTypeConverter.CreateNonVersionedReference <Patient>(repl.LoadProperty(o => o.SourceEntity)),
                    });
                }
            }

            var photo = model.LoadCollection(o => o.Extensions).FirstOrDefault(o => o.ExtensionTypeKey == ExtensionTypeKeys.JpegPhotoExtension);

            if (photo != null)
            {
                retVal.Photo = new List <Attachment>
                {
                    new Attachment
                    {
                        ContentType = "image/jpg",
                        Data        = photo.ExtensionValueXml
                    }
                };
            }

            return(retVal);
        }
예제 #6
0
        /// <summary>
        /// Get included objects
        /// </summary>
        protected override IEnumerable <Resource> GetIncludes(Core.Model.Roles.Patient resource, IEnumerable <IncludeInstruction> includePaths)
        {
            return(includePaths.SelectMany(includeInstruction =>
            {
                switch (includeInstruction.Type)
                {
                case ResourceType.Practitioner:
                    {
                        var rpHandler = FhirResourceHandlerUtil.GetMappersFor(ResourceType.Practitioner).FirstOrDefault();
                        switch (includeInstruction.JoinPath)
                        {
                        case "generalPractitioner":
                            return resource.LoadCollection(o => o.Relationships)
                            .Where(o => o.ClassificationKey != RelationshipClassKeys.ContainedObjectLink &&
                                   o.RelationshipTypeKey == EntityRelationshipTypeKeys.HealthcareProvider &&
                                   o.LoadProperty(r => r.TargetEntity) is Core.Model.Roles.Provider)
                            .Select(o => rpHandler.MapToFhir(o.TargetEntity));

                        default:
                            this.m_tracer.TraceError($"Cannot determine how to include {includeInstruction}");
                            throw new InvalidOperationException(this.m_localizationService.FormatString("error.type.InvalidOperation.cannotDetermine", new
                            {
                                param = includeInstruction
                            }));
                        }
                    }

                case ResourceType.Organization:
                    {
                        // Load all related persons and convert them
                        var rpHandler = FhirResourceHandlerUtil.GetMappersFor(ResourceType.Organization).FirstOrDefault();

                        switch (includeInstruction.JoinPath)
                        {
                        case "managingOrganization":
                            return resource.LoadCollection(o => o.Relationships)
                            .Where(o => o.ClassificationKey != RelationshipClassKeys.ContainedObjectLink &&
                                   o.RelationshipTypeKey == EntityRelationshipTypeKeys.Scoper &&
                                   o.LoadProperty(r => r.TargetEntity) is Core.Model.Entities.Organization)
                            .Select(o => rpHandler.MapToFhir(o.TargetEntity));

                        case "generalPractitioner":
                            return resource.LoadCollection(o => o.Relationships)
                            .Where(o => o.ClassificationKey != RelationshipClassKeys.ContainedObjectLink &&
                                   o.RelationshipTypeKey == EntityRelationshipTypeKeys.HealthcareProvider &&
                                   o.LoadProperty(r => r.TargetEntity) is Core.Model.Entities.Organization)
                            .Select(o => rpHandler.MapToFhir(o.TargetEntity));

                        default:
                            this.m_tracer.TraceError($"Cannot determine how to include {includeInstruction}");
                            throw new InvalidOperationException(this.m_localizationService.FormatString("error.type.InvalidOperation.cannotDetermine", new
                            {
                                param = includeInstruction
                            }));
                        }
                    }

                default:
                    this.m_tracer.TraceError($"{includeInstruction.Type} is not supported reverse include");
                    throw new InvalidOperationException(this.m_localizationService.GetString("error.type.NotSupportedException.userMessage"));
                }
            }));
        }
예제 #7
0
        /// <summary>
        /// Maps a FHIR patient resource to an HDSI patient.
        /// </summary>
        /// <param name="resource">The resource.</param>
        /// <returns>Returns the mapped model.</returns>
        protected override Core.Model.Roles.Patient MapToModel(Patient resource)
        {
            Core.Model.Roles.Patient patient = null;

            // Attempt to XRef
            if (Guid.TryParse(resource.Id, out Guid key))
            {
                patient = this.m_repository.Get(key);

                // Patient doesn't exist?
                if (patient == null)
                {
                    patient = new Core.Model.Roles.Patient
                    {
                        Key = key
                    };
                }
            }
            else if (resource.Identifier.Any())
            {
                foreach (var ii in resource.Identifier.Select(DataTypeConverter.ToEntityIdentifier))
                {
                    if (ii.LoadProperty(o => o.Authority).IsUnique)
                    {
                        patient = this.m_repository.Find(o => o.Identifiers.Where(i => i.AuthorityKey == ii.AuthorityKey).Any(i => i.Value == ii.Value)).FirstOrDefault();
                    }
                    if (patient != null)
                    {
                        break;
                    }
                }

                if (patient == null)
                {
                    patient = new Core.Model.Roles.Patient
                    {
                        Key = Guid.NewGuid()
                    };
                }
            }
            else
            {
                patient = new Core.Model.Roles.Patient
                {
                    Key = Guid.NewGuid()
                };
            }

            patient.Addresses             = resource.Address.Select(DataTypeConverter.ToEntityAddress).ToList();
            patient.CreationTime          = DateTimeOffset.Now;
            patient.GenderConceptKey      = resource.Gender == null ? null : DataTypeConverter.ToConcept(new Coding("http://hl7.org/fhir/administrative-gender", Hl7.Fhir.Utility.EnumUtility.GetLiteral(resource.Gender)))?.Key;
            patient.Identifiers           = resource.Identifier.Select(DataTypeConverter.ToEntityIdentifier).ToList();
            patient.LanguageCommunication = resource.Communication.Select(DataTypeConverter.ToLanguageCommunication).ToList();
            patient.Names            = resource.Name.Select(DataTypeConverter.ToEntityName).ToList();
            patient.StatusConceptKey = resource.Active == null || resource.Active == true ? StatusKeys.Active : StatusKeys.Inactive;
            patient.Telecoms         = resource.Telecom.Select(DataTypeConverter.ToEntityTelecomAddress).OfType <EntityTelecomAddress>().ToList();
            patient.Relationships    = resource.Contact.Select(r => DataTypeConverter.ToEntityRelationship(r, resource)).ToList();
            patient.Extensions       = resource.Extension.Select(o => DataTypeConverter.ToEntityExtension(o, patient)).ToList();

            patient.DateOfBirth = DataTypeConverter.ToDateTimeOffset(resource.BirthDate, out var dateOfBirthPrecision)?.DateTime;
            // TODO: fix
            // HACK: the date of birth precision CK only allows "Y", "M", or "D" for the precision value
            patient.DateOfBirthPrecision = dateOfBirthPrecision == DatePrecision.Full ? DatePrecision.Day : dateOfBirthPrecision;

            switch (resource.Deceased)
            {
            case FhirDateTime dtValue when !String.IsNullOrEmpty(dtValue.Value):
                patient.DeceasedDate = DataTypeConverter.ToDateTimeOffset(dtValue.Value, out var datePrecision)?.DateTime;
                // TODO: fix
                // HACK: the deceased date precision CK only allows "Y", "M", or "D" for the precision value
                patient.DeceasedDatePrecision = datePrecision == DatePrecision.Full ? DatePrecision.Day : datePrecision;
                break;

            case FhirBoolean boolValue when boolValue.Value.GetValueOrDefault():
                // we don't have a field for "deceased indicator" to say that the patient is dead, but we don't know that actual date/time of death
                // should find a better way to do this
                patient.DeceasedDate = DateTime.MinValue;

                patient.DeceasedDatePrecision = DatePrecision.Year;
                break;
            }

            switch (resource.MultipleBirth)
            {
            case FhirBoolean boolBirth when boolBirth.Value.GetValueOrDefault():
                patient.MultipleBirthOrder = 0;

                break;

            case Integer intBirth:
                patient.MultipleBirthOrder = intBirth.Value;
                break;
            }

            if (resource.GeneralPractitioner != null)
            {
                patient.Relationships.AddRange(resource.GeneralPractitioner.Select(r =>
                {
                    var referenceKey = DataTypeConverter.ResolveEntity <Core.Model.Roles.Provider>(r, resource) as Entity ?? DataTypeConverter.ResolveEntity <Core.Model.Entities.Organization>(r, resource);

                    if (referenceKey == null)
                    {
                        this.m_tracer.TraceError("Can't locate a registered general practitioner");
                        throw new KeyNotFoundException(m_localizationService.FormatString("error.type.KeyNotFoundException.cannotLocateRegistered", new
                        {
                            param = "general practitioner"
                        }));
                    }

                    return(new EntityRelationship(EntityRelationshipTypeKeys.HealthcareProvider, referenceKey));
                }));
            }
            if (resource.ManagingOrganization != null)
            {
                var referenceKey = DataTypeConverter.ResolveEntity <Core.Model.Entities.Organization>(resource.ManagingOrganization, resource);

                if (referenceKey == null)
                {
                    this.m_tracer.TraceError("Can't locate a registered managing organization");

                    throw new KeyNotFoundException(m_localizationService.FormatString("error.type.KeyNotFoundException.cannotLocateRegistered", new
                    {
                        param = "managing organization"
                    }));
                }

                patient.Relationships.Add(new EntityRelationship(EntityRelationshipTypeKeys.Scoper, referenceKey));
            }

            // Process contained related persons
            foreach (var itm in resource.Contained.OfType <RelatedPerson>())
            {
                var er = FhirResourceHandlerUtil.GetMappersFor(ResourceType.RelatedPerson).First().MapToModel(itm) as Core.Model.Entities.EntityRelationship;

                // Relationship bindings
                er.ClassificationKey = RelationshipClassKeys.ContainedObjectLink;
                er.SourceEntityKey   = patient.Key;

                // Now add rels to me
                patient.Relationships.Add(er);
            }

            // Links
            foreach (var lnk in resource.Link)
            {
                switch (lnk.Type.Value)
                {
                case Patient.LinkType.Replaces:
                {
                    // Find the victim
                    var replacee = DataTypeConverter.ResolveEntity <Core.Model.Roles.Patient>(lnk.Other, resource);

                    if (replacee == null)
                    {
                        this.m_tracer.TraceError($"Cannot locate patient referenced by {lnk.Type} relationship");
                        throw new KeyNotFoundException(m_localizationService.FormatString("error.messaging.fhir.patientResource.cannotLocatePatient", new
                            {
                                param = lnk.Type
                            }));
                    }

                    replacee.StatusConceptKey = StatusKeys.Obsolete;
                    patient.Relationships.Add(new EntityRelationship(EntityRelationshipTypeKeys.Replaces, replacee));
                    break;
                }

                case Patient.LinkType.ReplacedBy:
                {
                    // Find the new
                    var replacer = DataTypeConverter.ResolveEntity <Core.Model.Roles.Patient>(lnk.Other, resource);

                    if (replacer == null)
                    {
                        this.m_tracer.TraceError($"Cannot locate patient referenced by {lnk.Type} relationship");
                        throw new KeyNotFoundException(m_localizationService.FormatString("error.messaging.fhir.patientResource.cannotLocatePatient", new
                            {
                                param = lnk.Type
                            }));
                    }

                    patient.StatusConceptKey = StatusKeys.Obsolete;
                    patient.Relationships.Add(new EntityRelationship(EntityRelationshipTypeKeys.Replaces, patient)
                        {
                            HolderKey = replacer.Key
                        });
                    break;
                }

                case Patient.LinkType.Seealso:
                {
                    var referee = DataTypeConverter.ResolveEntity <Entity>(lnk.Other, resource);        // We use Entity here in lieu Patient since the code below can handle the MDM layer

                    // Is this a current MDM link?
                    if (referee.GetTag(FhirConstants.PlaceholderTag) == "true")         // The referee wants us to become the data
                    {
                        patient.Key = referee.Key;
                    }
                    else if (referee.LoadCollection(o => o.Relationships).Any(r => r.RelationshipTypeKey == MDM_MASTER_LINK) &&
                             referee.GetTag("$mdm.type") == "M")       // HACK: This is a master and someone is attempting to point another record at it
                    {
                        patient.Relationships.Add(new EntityRelationship()
                            {
                                RelationshipTypeKey = MDM_MASTER_LINK,
                                SourceEntityKey     = referee.Key,
                                TargetEntityKey     = patient.Key
                            });
                    }
                    else
                    {
                        patient.Relationships.Add(new EntityRelationship(EntityRelationshipTypeKeys.EquivalentEntity, referee));
                    }
                    break;
                }

                case Patient.LinkType.Refer:     // This points to a more detailed view of the patient
                {
                    var referee = DataTypeConverter.ResolveEntity <Entity>(lnk.Other, resource);
                    if (referee.GetTag("$mdm.type") == "M")         // HACK: MDM User is attempting to point this at another Master (note: THE MDM LAYER WON'T LIKE THIS)
                    {
                        patient.Relationships.Add(new EntityRelationship(MDM_MASTER_LINK, referee));
                    }
                    else
                    {
                        this.m_tracer.TraceError($"Setting refer relationships to source of truth is not supported");
                        throw new NotSupportedException(m_localizationService.GetString("error.type.NotSupportedException.userMessage"));
                    }

                    break;         // TODO: These are special cases of references
                }
                }
            }

            if (resource.Photo != null && resource.Photo.Any())
            {
                patient.Extensions.RemoveAll(o => o.ExtensionTypeKey == ExtensionTypeKeys.JpegPhotoExtension);
                patient.Extensions.Add(new EntityExtension(ExtensionTypeKeys.JpegPhotoExtension, resource.Photo.First().Data));
            }

            return(patient);
        }