/// <summary> /// Maps the places. /// </summary> /// <param name="csdFacilities">The CSD facilities.</param> /// <returns>Returns a list of places.</returns> private static IEnumerable <Entity> MapPlaces(IEnumerable <facility> csdFacilities, IEnumerable <organization> csdOrgaizations, CsdOptions options) { var places = new List <Entity>(); int idx = 0; foreach (var facility in csdFacilities) { var place = GetOrCreateEntity <Place>(facility.entityID, options.EntityUidAuthority, options); if (place == null) { ShowErrorOnNotFound($"Facility with entityID {facility.entityID} not found in database and it cannot be created"); } if (!options.RelationshipsOnly) { place.ClassConceptKey = EntityClassKeys.ServiceDeliveryLocation; place.StatusConceptKey = StatusKeys.Active; // map addresses if (facility.address?.Any() == true) { ShowInfoMessage("Mapping place addresses..."); var addresses = ReconcileVersionedAssociations(place.Addresses, facility.address?.Select(a => MapEntityAddress(a, new Uri(AddressTypeCodeSystem)))).Cast <EntityAddress>(); place.Addresses.AddRange(addresses); } // map latitude if (facility.geocode?.latitude != null) { ShowInfoMessage("Mapping place latitude..."); place.Lat = Convert.ToDouble(facility.geocode.latitude); } // map longitude if (facility.geocode?.longitude != null) { ShowInfoMessage("Mapping place longitude..."); place.Lng = Convert.ToDouble(facility.geocode.longitude); } // map extensions if (facility.extension?.Any(o => o.type != options.FacilityTypeExtension) == true) { ShowInfoMessage("Mapping place extensions..."); var extensions = ReconcileVersionedAssociations(place.Extensions, facility.extension.Select(e => MapEntityExtension(e.urn, e.Any.InnerText))).Cast <EntityExtension>(); place.Extensions.AddRange(extensions); } if (facility.extension.Any(o => o.type == options.FacilityTypeExtension) && !place.TypeConceptKey.HasValue) { var typeExtension = facility.extension.FirstOrDefault(o => o.type == options.FacilityTypeExtension); place.TypeConceptKey = MapCodedType(typeExtension.Any.InnerText, typeExtension.urn + ":facility_type")?.Key; } // map identifiers if (facility.otherID?.Any() == true) { ShowInfoMessage("Mapping place identifiers..."); var identifiers = ReconcileVersionedAssociations(place.Identifiers, facility.otherID.Select(MapEntityIdentifier)).Cast <EntityIdentifier>(); place.Identifiers.AddRange(identifiers); } Entity parent = null; // map parent relationships if (facility.parent?.entityID != null) { ShowInfoMessage("Mapping place parent relationships..."); place.Relationships.RemoveAll(r => r.RelationshipTypeKey == EntityRelationshipTypeKeys.Parent); parent = GetOrCreateEntity <Place>(facility.parent.entityID, options.EntityUidAuthority, options); place.Relationships.Add(new EntityRelationship(EntityRelationshipTypeKeys.Parent, parent)); } else if (options.HackParentFind) // HACK: for Tanzania { string hackParentCode = options.ParentOrgCode; // We go to the org var serviceOrg = csdOrgaizations.FirstOrDefault(o => o.entityID == facility.organizations?.FirstOrDefault()?.entityID); var parentFacility = facility; // Then we use the parent until we hit the parent code while (serviceOrg != null) { serviceOrg = csdOrgaizations.FirstOrDefault(o => o.entityID == serviceOrg.parent?.entityID); if (serviceOrg == null) { break; } parentFacility = csdFacilities.FirstOrDefault(f => f.organizations.Any(o => o.entityID == serviceOrg?.entityID) && (f.codedType?.Any(c => options.ParentCodeType.Contains(c.code)) == true || !String.IsNullOrEmpty(options.FacilityTypeExtension) && options.ParentCodeType.Contains(f.extension.FirstOrDefault(o => o.type == options.FacilityTypeExtension)?.Any.InnerText))); // Find the facility which services that if (string.IsNullOrEmpty(hackParentCode) || serviceOrg.codedType.Any(o => o.code == hackParentCode)) { break; } } // Now we want to assign the parent if (parentFacility != null) { place.Relationships.RemoveAll(r => r.RelationshipTypeKey == EntityRelationshipTypeKeys.Parent); parent = GetOrCreateEntity <Place>(parentFacility.entityID, options.EntityUidAuthority, options); place.Relationships.Add(new EntityRelationship(EntityRelationshipTypeKeys.Parent, parent)); facility.parent = new uniqueID() { entityID = parentFacility.entityID }; } } else if (facility.extension.Any(o => o.type == "DistrictVaccinationStore")) // DVS extension { var dvsExtension = facility.extension.FirstOrDefault(o => o.type == "DVS"); var dvsPlace = GetOrCreateEntity <Place>(dvsExtension.Any.Attributes["entityID"]?.Value, options.EntityUidAuthority, options); if (dvsPlace != null) { place.Relationships.Add(new EntityRelationship(EntityRelationshipTypeKeys.Parent, dvsPlace.Key)); } if (!entityMap.ContainsKey(dvsExtension.Any.Attributes["entityID"]?.Value)) { entityMap.Add(dvsExtension.Any.Attributes["entityID"]?.Value, dvsPlace); } } // map coded type if (facility.codedType?.Any() == true) { ShowInfoMessage("Mapping place type concept..."); // we don't support multiple types for a place at the moment, so we only take the first one // TODO: cleanup place.TypeConceptKey = MapCodedType(facility.codedType[0].code, facility.codedType[0].codingScheme)?.Key; } // map primary name if (facility.primaryName != null) { ShowInfoMessage("Mapping place names..."); place.Names.RemoveAll(c => c.NameUseKey == NameUseKeys.OfficialRecord); place.Names.Add(new EntityName(NameUseKeys.OfficialRecord, facility.primaryName)); } // map other names if (facility.otherName?.Any() == true) { ShowInfoMessage("Mapping place additional names..."); var names = ReconcileVersionedAssociations(place.Names, facility.otherName.Select(c => new EntityName(NameUseKeys.Assigned, c.Value))).Cast <EntityName>(); place.Names.AddRange(names); } // map contact points if (facility.contactPoint?.Any() == true) { ShowInfoMessage("Mapping place telecommunications..."); var telecoms = ReconcileVersionedAssociations(place.Telecoms, facility.contactPoint.Select(c => MapContactPoint(TelecomAddressUseKeys.Public, c))).Cast <EntityTelecomAddress>(); place.Telecoms.AddRange(telecoms); } // map status concept if (facility.record?.status != null) { ShowInfoMessage("Mapping place status..."); place.StatusConceptKey = MapStatusCode(facility.record.status, "http://openiz.org/csd/CSD-FacilityStatusCode"); } if (parent != null && places.Any(c => c.Identifiers.All(i => i.Value != facility.parent?.entityID))) { places.Add(parent); } if (parent != null && !entityMap.ContainsKey(facility.parent?.entityID)) { entityMap.Add(facility.parent.entityID, parent); } } // map organization relationships - These are the villages which the place supports if (facility.organizations?.Any() == true) { ShowInfoMessage("Mapping serviced organizations relationships..."); CreateEntityRelationships(place, facility, csdOrgaizations, options); } if (facility?.record.created != null) { place.CreationTime = facility.record.created; } places.Add(place); if (!entityMap.ContainsKey(facility.entityID)) { entityMap.Add(facility.entityID, place); } Console.ForegroundColor = ConsoleColor.Magenta; Console.WriteLine($"Mapped place: ({idx++}/{csdFacilities.Count()}) {place.Key.Value} {string.Join(" ", place.Names.SelectMany(n => n.Component).Select(c => c.Value))}"); Console.ResetColor(); } return(places); }
/// <summary> /// Create entity relationships /// </summary> private static IEnumerable <EntityRelationship> CreateEntityRelationships(Place place, facility facility, IEnumerable <organization> csdOrganizations, CsdOptions options) { var retVal = new List <EntityRelationship>(); // Cascade organizations foreach (var org in facility.organizations) { if (String.IsNullOrEmpty(org.entityID)) { continue; } var linkedOrgs = new List <String>() { org.entityID }; if (options.CascadeAssignedFacilities) { linkedOrgs = linkedOrgs.Union(csdOrganizations.Where(o => o.parent?.entityID == org.entityID).Select(o => o.entityID)).ToList(); } foreach (var lo in linkedOrgs) { var villageServiced = GetOrCreateEntity <Place>(lo, options.EntityUidAuthority, options); if (lo == linkedOrgs.FirstOrDefault() && place.Addresses == null) { var addr = villageServiced.Addresses.FirstOrDefault(o => o.AddressUseKey == AddressUseKeys.Direct)?.Clone() as EntityAddress; place.Addresses.Add(addr); } if (options.NoDbCheck || !options.RelationshipsOnly || // We need to create everything anyways ApplicationContext.Current.GetService <IDataPersistenceService <EntityRelationship> >().Count(x => x.SourceEntityKey == villageServiced.Key && x.TargetEntityKey == place.Key, AuthenticationContext.SystemPrincipal) == 0) { var er = new EntityRelationship(EntityRelationshipTypeKeys.DedicatedServiceDeliveryLocation, place.Key.Value) { SourceEntityKey = villageServiced.Key }; villageServiced.Relationships.Add(er); retVal.Add(er); if (!entityMap.ContainsKey(lo)) { entityMap.Add(lo, villageServiced); } } } } return(retVal); }
/// <summary> /// Maps the providers. /// </summary> /// <param name="csdProviders">The CSD providers.</param> /// <returns>Returns a list of providers.</returns> private static IEnumerable <Provider> MapProviders(IEnumerable <provider> csdProviders, CsdOptions options) { var providers = new List <Provider>(); foreach (var csdProvider in csdProviders) { var provider = GetOrCreateEntity <Provider>(csdProvider.entityID, options.EntityUidAuthority, options); // map addresses if (csdProvider.demographic?.address?.Any() == true) { ShowInfoMessage("Mapping provider addresses..."); var addresses = ReconcileVersionedAssociations(provider.Addresses, csdProvider.demographic.address?.Select(a => MapEntityAddress(a, new Uri(AddressTypeCodeSystem)))).Cast <EntityAddress>(); provider.Addresses.AddRange(addresses); } // map date of birth if (csdProvider.demographic?.dateOfBirthSpecified == true) { ShowInfoMessage("Mapping provider date of birth..."); provider.DateOfBirth = csdProvider.demographic.dateOfBirth; } // map specialty key if (csdProvider.specialty?.Any() == true) { ShowInfoMessage("Mapping provider specialty concept..."); // we don't support multiple specialties for a provider at the moment, so we only take the first one // TODO: cleanup provider.ProviderSpecialtyKey = MapCodedType(csdProvider.specialty[0].code, csdProvider.specialty[0].codingScheme)?.Key; } // map type concept if (csdProvider.codedType?.Any() == true) { ShowInfoMessage("Mapping provider type concept..."); // we don't support multiple types for a provider at the moment, so we only take the first one // TODO: cleanup provider.TypeConceptKey = MapCodedType(csdProvider.codedType[0].code, csdProvider.codedType[0].codingScheme)?.Key; } // map extensions if (csdProvider.extension?.Any() == true) { ShowInfoMessage("Mapping provider extensions..."); var extensions = ReconcileVersionedAssociations(provider.Extensions, csdProvider.extension.Select(e => MapEntityExtension(e.urn, e.Any.InnerText))).Cast <EntityExtension>(); provider.Extensions.AddRange(extensions); } // map identifiers if (csdProvider.otherID?.Any() == true) { ShowInfoMessage("Mapping provider identifiers..."); var identifiers = ReconcileVersionedAssociations(provider.Identifiers, csdProvider.otherID.Select(MapEntityIdentifier)).Cast <EntityIdentifier>(); provider.Identifiers.AddRange(identifiers); } // map language communication if (csdProvider.language?.Any() == true) { ShowInfoMessage("Mapping provider languages..."); var languages = ReconcileVersionedAssociations(provider.LanguageCommunication, csdProvider.language.Select(MapLanguageCommunication)).Cast <PersonLanguageCommunication>(); provider.LanguageCommunication.AddRange(languages); } // map names if (csdProvider.demographic?.name?.Any() == true) { ShowInfoMessage("Mapping provider names..."); provider.Names.RemoveAll(c => c.NameUseKey == NameUseKeys.OfficialRecord); provider.Names.AddRange(csdProvider.demographic.name.Select(c => MapEntityNamePerson(NameUseKeys.OfficialRecord, c))); } // map status concept if (csdProvider.record?.status != null) { ShowInfoMessage("Mapping provider status..."); provider.StatusConceptKey = MapStatusCode(csdProvider.record.status, "http://openiz.org/csd/CSD-ProviderStatusCodes"); } // map contact points if (csdProvider.demographic?.contactPoint?.Any() == true) { ShowInfoMessage("Mapping provider telecommunications..."); var telecoms = ReconcileVersionedAssociations(provider.Telecoms, csdProvider.demographic.contactPoint.Select(c => MapContactPoint(TelecomAddressUseKeys.Public, c))).Cast <EntityTelecomAddress>(); provider.Telecoms.AddRange(telecoms); } providers.Add(provider); Console.ForegroundColor = ConsoleColor.Magenta; Console.WriteLine($"Mapped provider: {provider.Key.Value} {string.Join(" ", provider.Names.SelectMany(n => n.Component).Select(c => c.Value))}"); Console.ResetColor(); } return(providers); }
/// <summary> /// Maps the organizations. /// </summary> /// <param name="csdOrganizations">The CSD organizations.</param> /// <returns>Returns a list of organizations.</returns> private static IEnumerable <Entity> MapOrganizations(IEnumerable <organization> csdOrganizations, CsdOptions options) { var organizations = new List <Entity>(); IConceptRepositoryService icpr = ApplicationContext.Current.GetService <IConceptRepositoryService>(); var idx = 0; foreach (var csdOrganization in csdOrganizations) { Entity importItem = null; if (!options.OrganizationsAsPlaces) { importItem = GetOrCreateEntity <Organization>(csdOrganization.entityID, options.EntityUidAuthority, options); } else { importItem = GetOrCreateEntity <Place>(csdOrganization.entityID, options.EntityUidAuthority, options); // Set proper class code for the place importItem.ClassConceptKey = EntityClassKeys.Place; } // addresses if (csdOrganization.address?.Any() == true) { ShowInfoMessage("Mapping organization addresses..."); var addresses = ReconcileVersionedAssociations(importItem.Addresses, csdOrganization.address?.Select(a => MapEntityAddress(a, new Uri(AddressTypeCodeSystem)))).Cast <EntityAddress>(); importItem.Addresses.AddRange(addresses); } // If organization as place if (options.OrganizationsAsPlaces) // Import village hiearchy as address { importItem.Addresses = new List <EntityAddress>() { TraversePlaceHeirarchyAsAddress(csdOrganization, csdOrganizations) }; } // map type concept if (csdOrganization.codedType?.Any() == true) { ShowInfoMessage("Mapping organization type concept..."); // we don't support multiple specialties for a organization at the moment, so we only take the first one // TODO: cleanup var typeConcept = MapCodedType(csdOrganization.codedType[0].code, csdOrganization.codedType[0].codingScheme); importItem.TypeConceptKey = typeConcept?.Key; // We now need to create the proper class concept key Guid classKey = Guid.Empty; if (typeConcept != null && !m_classCodeTypeMaps.TryGetValue(typeConcept.Key.Value, out classKey)) { // Look for a relationship in the EntityClass var adptConcept = icpr.FindConcepts(o => o.Relationship.Any(r => r.SourceEntityKey == typeConcept.Key) && o.ConceptSets.Any(s => s.Key == ConceptSetKeys.EntityClass)).FirstOrDefault(); if (adptConcept != null) { importItem.ClassConceptKey = adptConcept.Key; } else if (adptConcept == null && typeConcept.ConceptSetsXml.Contains(ConceptSetKeys.EntityClass) || icpr.FindConcepts(o => o.ConceptSets.Any(c => c.Key == ConceptSetKeys.EntityClass) && o.Key == typeConcept.Key).Any()) { importItem.ClassConceptKey = typeConcept.Key; } if (importItem.ClassConceptKey.HasValue) { m_classCodeTypeMaps.Add(typeConcept.Key.Value, importItem.ClassConceptKey.Value); } else { m_classCodeTypeMaps.Add(importItem.ClassConceptKey.Value, Guid.Empty); } classKey = importItem.ClassConceptKey ?? EntityClassKeys.Place; } importItem.ClassConceptKey = classKey; } // map specializations if (csdOrganization.specialization?.Any() == true && importItem is Organization) { ShowInfoMessage("Mapping organization industry concept..."); // we don't support multiple industry values for a organization at the moment, so we only take the first one // TODO: cleanup (importItem as Organization).IndustryConceptKey = MapCodedType(csdOrganization.specialization[0].code, csdOrganization.specialization[0].codingScheme)?.Key; } // map extensions if (csdOrganization.extension?.Any() == true) { ShowInfoMessage("Mapping organization extensions..."); var extensions = ReconcileVersionedAssociations(importItem.Extensions, csdOrganization.extension.Select(e => MapEntityExtension(e.urn, e.Any.InnerText))).Cast <EntityExtension>(); importItem.Extensions.AddRange(extensions); } // map identifiers if (csdOrganization.otherID?.Any() == true) { ShowInfoMessage("Mapping organization identifiers..."); var identifiers = ReconcileVersionedAssociations(importItem.Identifiers, csdOrganization.otherID.Select(MapEntityIdentifier)).Cast <EntityIdentifier>(); importItem.Identifiers.AddRange(identifiers); } Entity parent = null; // map parent relationships if (csdOrganization.parent?.entityID != null) { ShowInfoMessage("Mapping organization parent relationships..."); importItem.Relationships.RemoveAll(r => r.RelationshipTypeKey == EntityRelationshipTypeKeys.Parent); if (!options.OrganizationsAsPlaces) { parent = GetOrCreateEntity <Organization>(csdOrganization.parent.entityID, options.EntityUidAuthority, options); } else { parent = GetOrCreateEntity <Place>(csdOrganization.parent.entityID, options.EntityUidAuthority, options); } importItem.Relationships.Add(new EntityRelationship(EntityRelationshipTypeKeys.Parent, parent)); } // map primary name if (csdOrganization.primaryName != null) { ShowInfoMessage("Mapping organization names..."); importItem.Names.RemoveAll(c => c.NameUseKey == NameUseKeys.OfficialRecord); importItem.Names.Add(new EntityName(NameUseKeys.OfficialRecord, csdOrganization.primaryName)); } // map names if (csdOrganization.otherName?.Any() == true) { ShowInfoMessage("Mapping organization additional names..."); var names = ReconcileVersionedAssociations(importItem.Names, csdOrganization.otherName.Select(c => MapEntityNameOrganization(NameUseKeys.Assigned, c))).Cast <EntityName>(); importItem.Names.AddRange(names); } // map tags if (csdOrganization.record?.sourceDirectory != null) { ShowInfoMessage("Mapping organization tags..."); importItem.Tags.RemoveAll(t => t.TagKey == "sourceDirectory"); importItem.Tags.Add(new EntityTag("sourceDirectory", csdOrganization.record.sourceDirectory)); } // map contacts if (csdOrganization.contact?.Any() == true) { ShowInfoMessage("Mapping organization contact relationships..."); // HACK importItem.Relationships.RemoveAll(r => r.RelationshipTypeKey == EntityRelationshipTypeKeys.Contact); importItem.Relationships.AddRange(csdOrganization.contact.Select(o => MapEntityRelationshipOrganizationContact(o, options))); } // map contact points if (csdOrganization.contactPoint?.Any() == true) { ShowInfoMessage("Mapping organization telecommunications..."); var telecoms = ReconcileVersionedAssociations(importItem.Telecoms, csdOrganization.contactPoint.Select(c => MapContactPoint(TelecomAddressUseKeys.Public, c))).Cast <EntityTelecomAddress>(); importItem.Telecoms.AddRange(telecoms); } // map status concept if (csdOrganization.record?.status != null) { ShowInfoMessage("Mapping organization status..."); importItem.StatusConceptKey = MapStatusCode(csdOrganization.record.status, "http://openiz.org/csd/CSD-OrganizationStatusCodes"); } organizations.Add(importItem); if (parent != null && organizations.Any(c => c.Identifiers.All(i => i.Value != csdOrganization.parent?.entityID))) { organizations.Add(parent); } if (!entityMap.ContainsKey(csdOrganization.entityID)) { entityMap.Add(csdOrganization.entityID, importItem); } if (parent != null && !entityMap.ContainsKey(csdOrganization.parent?.entityID)) { entityMap.Add(csdOrganization.parent?.entityID, parent); } Console.ForegroundColor = ConsoleColor.Magenta; Console.WriteLine($"Mapped organization {(options.OrganizationsAsPlaces ? "as place" : "")} ({idx++}/{csdOrganizations.Count()}): {importItem.Key.Value} {string.Join(" ", importItem.Names.SelectMany(n => n.Component).Select(c => c.Value))}"); Console.ResetColor(); } return(organizations); }
/// <summary> /// Maps the entity relationship organization contact. /// </summary> /// <param name="contact">The contact.</param> /// <returns>Returns the mapped entity relationship from an organization contact instance.</returns> private static EntityRelationship MapEntityRelationshipOrganizationContact(organizationContact contact, CsdOptions options) { EntityRelationship entityRelationship = null; if (contact.Item.GetType() == typeof(uniqueID)) { var csdProvider = contact.Item as uniqueID; var provider = GetOrCreateEntity <Provider>(csdProvider.entityID, options.EntityUidAuthority, options); entityRelationship = new EntityRelationship(EntityRelationshipTypeKeys.Contact, provider); } else if (contact.Item.GetType() == typeof(person)) { var csdPerson = contact.Item as person; var personService = ApplicationContext.Current.GetService <IPersonRepositoryService>(); // TODO: fix to search the person by address, name, date of birth, and gender, to find an existing person before creating a new one // we are creating a new person here, because the person class in CSD doesn't have an entityID // property for us to use to lookup the person to see if they exist. var person = new Person { Addresses = csdPerson.address?.Select(a => MapEntityAddress(a, new Uri(AddressTypeCodeSystem))).ToList() ?? new List <EntityAddress>(), Key = Guid.NewGuid() }; if (csdPerson.dateOfBirthSpecified) { person.DateOfBirth = csdPerson.dateOfBirth; } // map names if (csdPerson.name?.Any() == true) { person.Names.RemoveAll(c => c.NameUseKey == NameUseKeys.OfficialRecord); person.Names.AddRange(csdPerson.name.Select(c => MapEntityNamePerson(NameUseKeys.OfficialRecord, c))); } // map telecommunications if (csdPerson.contactPoint?.Any() == true) { person.Telecoms.RemoveAll(c => c.AddressUseKey == TelecomAddressUseKeys.Public); person.Telecoms.AddRange(csdPerson.contactPoint?.Select(c => MapContactPoint(TelecomAddressUseKeys.Public, c))); } entityRelationship = new EntityRelationship(EntityRelationshipTypeKeys.Contact, person); } else { ShowErrorOnNotFound($"Error, {emergencyMessage} {nameof(organizationContact.Item)} is not of type: {nameof(person)} or {nameof(uniqueID)}"); } return(entityRelationship); }
/// <summary> /// Looks up the entity by entity identifier. This will also create a new entity if one is not found. /// </summary> /// <typeparam name="T">The type of entity to lookup.</typeparam> /// <param name="entityId">The entity identifier.</param> /// <returns>Returns the entity instance.</returns> private static T GetOrCreateEntity <T>(string entityId, String authorityName, CsdOptions options) where T : Entity, new() { Entity entity; if (entityMap.TryGetValue(entityId, out entity)) { return(entity as T); } var entityService = ApplicationContext.Current.GetService <IDataPersistenceService <T> >(); int totalResults = 0; if (!options.NoDbCheck) { entity = entityService.Query(c => c.Identifiers.Any(i => i.Authority.DomainName == authorityName && i.Value == entityId) && c.ObsoletionTime == null, 0, 1, AuthenticationContext.SystemPrincipal, out totalResults).FirstOrDefault(); } if (totalResults > 1) { ShowWarningMessage($"Warning, found multiple entities with the same entityID: '{entityId}', will default to: '{entity.Key.Value}' {Environment.NewLine}"); } if (entity != null || options.NoCreate) { return((T)entity); } ShowWarningMessage("Warning, ENTITY NOT FOUND, will create one"); // setup basic properties of the entity instance entity = new T { CreationTime = DateTimeOffset.Now, Key = Guid.NewGuid(), StatusConceptKey = StatusKeys.Active, Tags = new List <EntityTag> { new EntityTag(ImportedDataTag, "true") } }; entity.Identifiers.Add(new EntityIdentifier(authorityName, entityId)); return((T)entity); }