/// <summary> /// Resume a pending export: Checks if the export is complete. If the export is complete, then download the export, process the addresses and sent a notification. /// </summary> /// <param name="sparkDataConfig">The spark data configuration.</param> public void PendingExport(SparkDataConfig sparkDataConfig = null) { if (sparkDataConfig == null) { sparkDataConfig = GetSettings(); } SparkDataApi.SparkDataApi sparkDataApi = new SparkDataApi.SparkDataApi(); var credentials = sparkDataApi.NcoaGetCredentials(sparkDataConfig.SparkDataApiKey); var ncoaApi = new NcoaApi(credentials); if (!ncoaApi.IsReportExportCreated(sparkDataConfig.NcoaSettings.FileName)) { return; } List <NcoaReturnRecord> ncoaReturnRecords; ncoaApi.DownloadExport(sparkDataConfig.NcoaSettings.CurrentReportExportKey, out ncoaReturnRecords); var ncoaHistoryList = ncoaReturnRecords.Select(r => r.ToNcoaHistory()).ToList(); FilterDuplicateLocations(ncoaHistoryList); // Making sure that the database is empty to avoid adding duplicate data. using (var rockContext = new RockContext()) { NcoaHistoryService ncoaHistoryService = new NcoaHistoryService(rockContext); ncoaHistoryService.DeleteRange(ncoaHistoryService.Queryable()); rockContext.SaveChanges(); } if (ncoaReturnRecords != null && ncoaReturnRecords.Count != 0) { using (var rockContext = new RockContext()) { var ncoaHistoryService = new NcoaHistoryService(rockContext); ncoaHistoryService.AddRange(ncoaHistoryList); rockContext.SaveChanges(); } } ProcessNcoaResults(sparkDataConfig); sparkDataApi.NcoaCompleteReport(sparkDataConfig.SparkDataApiKey, sparkDataConfig.NcoaSettings.FileName, sparkDataConfig.NcoaSettings.CurrentReportExportKey); //Notify group SentNotification(sparkDataConfig, "finished"); sparkDataConfig.NcoaSettings.LastRunDate = RockDateTime.Now; sparkDataConfig.NcoaSettings.CurrentReportStatus = "Complete"; sparkDataConfig.NcoaSettings.FileName = null; SaveSettings(sparkDataConfig); }
/// <summary> /// Processes the NCOA results: Mark all individual move addresses as previous, add the new address as current; and processed. /// If minMoveDistance is specified, mark the individual as inactive if the individual moved further than the specified distance. /// </summary> /// <param name="inactiveReason">The inactive reason.</param> /// <param name="minMoveDistance">The minimum move distance.</param> /// <param name="homeValueId">The home value identifier.</param> /// <param name="previousValueId">The previous value identifier.</param> private void ProcessNcoaResultsIndividualMove(DefinedValueCache inactiveReason, decimal?minMoveDistance, int?homeValueId, int?previousValueId) { List <int> ncoaIds = null; // Process 'Move' NCOA Types (For the remaining Individual move types that weren't updated with the family move) using (var rockContext = new RockContext()) { ncoaIds = new NcoaHistoryService(rockContext) .Queryable().AsNoTracking() .Where(n => n.Processed == Processed.NotProcessed && n.NcoaType == NcoaType.Move && n.MoveType == MoveType.Individual) .Select(n => n.Id) .ToList(); } foreach (int id in ncoaIds) { using (var rockContext = new RockContext()) { // Get the NCOA record and make sure it still hasn't been processed var ncoaHistory = new NcoaHistoryService(rockContext).Get(id); if (ncoaHistory != null && ncoaHistory.Processed == Processed.NotProcessed) { var ncoaHistoryService = new NcoaHistoryService(rockContext); var groupMemberService = new GroupMemberService(rockContext); var personAliasService = new PersonAliasService(rockContext); var groupService = new GroupService(rockContext); var groupLocationService = new GroupLocationService(rockContext); var locationService = new LocationService(rockContext); var personService = new PersonService(rockContext); var changes = new History.HistoryChangeList(); // Default the status to requiring a manual update (we might change this though) ncoaHistory.Processed = Processed.ManualUpdateRequired; // Find the existing family var family = groupService.Get(ncoaHistory.FamilyId); // If there's only one person in the family if (family.Members.Count == 1) { // And that person is the same as the move record's person then we can process it. var personAlias = personAliasService.Get(ncoaHistory.PersonAliasId); var familyMember = family.Members.First(); if (personAlias != null && familyMember.PersonId == personAlias.PersonId) { // If were able to mark their existing address as previous and add a new updated Home address, // then set the status to complete (otherwise leave it as needing a manual update). var previousGroupLocation = MarkAsPreviousLocation(ncoaHistory, groupLocationService, previousValueId, changes); if (previousGroupLocation != null) { if (AddNewHomeLocation(ncoaHistory, locationService, groupLocationService, homeValueId, changes, previousGroupLocation.IsMailingLocation, previousGroupLocation.IsMappedLocation)) { ncoaHistory.Processed = Processed.Complete; // Look for any other moves for the same person to same address, and set their process to complete also foreach (var ncoaIndividual in ncoaHistoryService .Queryable().Where(n => n.Processed == Processed.NotProcessed && n.NcoaType == NcoaType.Move && n.MoveType == MoveType.Individual && n.PersonAliasId == ncoaHistory.PersonAliasId && n.Id != ncoaHistory.Id && n.UpdatedStreet1 == ncoaHistory.UpdatedStreet1)) { ncoaIndividual.Processed = Processed.Complete; } // If there were any changes, write to history and check to see if person should be inactivated if (changes.Any()) { if (ncoaHistory.MoveDistance.HasValue && minMoveDistance.HasValue && ncoaHistory.MoveDistance.Value >= minMoveDistance.Value) { History.HistoryChangeList personChanges; personService.InactivatePerson(familyMember.Person, inactiveReason, $"Received a Change of Address (NCOA) notice that was for more than {minMoveDistance} miles away.", out personChanges); if (personChanges.Any()) { HistoryService.SaveChanges( rockContext, typeof(Person), SystemGuid.Category.HISTORY_PERSON_DEMOGRAPHIC_CHANGES.AsGuid(), familyMember.PersonId, personChanges, false); } } HistoryService.SaveChanges( rockContext, typeof(Person), SystemGuid.Category.HISTORY_PERSON_FAMILY_CHANGES.AsGuid(), familyMember.PersonId, changes, family.Name, typeof(Group), family.Id, false); } } } } } rockContext.SaveChanges(); } } } }
/// <summary> /// Processes the NCOA results: Mark all family move addresses as previous, add the new address as current; and processed. /// If minMoveDistance is specified, mark the family as inactive if the family moved further than the specified distance. /// </summary> /// <param name="inactiveReason">The inactive reason.</param> /// <param name="minMoveDistance">The minimum move distance.</param> /// <param name="homeValueId">The home value identifier.</param> /// <param name="previousValueId">The previous value identifier.</param> private void ProcessNcoaResultsFamilyMove(DefinedValueCache inactiveReason, decimal?minMoveDistance, int?homeValueId, int?previousValueId) { List <int> ncoaIds = null; // Process 'Move' NCOA Types (The 'Family' move types will be processed first) using (var rockContext = new RockContext()) { ncoaIds = new NcoaHistoryService(rockContext) .Queryable().AsNoTracking() .Where(n => n.Processed == Processed.NotProcessed && n.NcoaType == NcoaType.Move && n.MoveType == MoveType.Family) .Select(n => n.Id) .ToList(); } foreach (int id in ncoaIds) { using (var rockContext = new RockContext()) { // Get the NCOA record and make sure it still hasn't been processed var ncoaHistory = new NcoaHistoryService(rockContext).Get(id); if (ncoaHistory != null && ncoaHistory.Processed == Processed.NotProcessed) { var ncoaHistoryService = new NcoaHistoryService(rockContext); var groupService = new GroupService(rockContext); var groupLocationService = new GroupLocationService(rockContext); var locationService = new LocationService(rockContext); var personService = new PersonService(rockContext); var familyChanges = new History.HistoryChangeList(); ncoaHistory.Processed = Processed.ManualUpdateRequired; // If we're able to mark the existing address as previous and successfully create a new home address.. var previousGroupLocation = MarkAsPreviousLocation(ncoaHistory, groupLocationService, previousValueId, familyChanges); if (previousGroupLocation != null) { if (AddNewHomeLocation(ncoaHistory, locationService, groupLocationService, homeValueId, familyChanges, previousGroupLocation.IsMailingLocation, previousGroupLocation.IsMappedLocation)) { // set the status to 'Complete' ncoaHistory.Processed = Processed.Complete; // Look for any other moves for the same family and to same address, and set their status to complete as well foreach (var ncoaIndividual in ncoaHistoryService .Queryable().Where(n => n.Processed == Processed.NotProcessed && n.NcoaType == NcoaType.Move && n.FamilyId == ncoaHistory.FamilyId && n.Id != ncoaHistory.Id && n.UpdatedStreet1 == ncoaHistory.UpdatedStreet1)) { ncoaIndividual.Processed = Processed.Complete; } // If there were any changes, write to history and check to see if person should be inactivated if (familyChanges.Any()) { var family = groupService.Get(ncoaHistory.FamilyId); if (family != null) { foreach (var fm in family.Members) { if (ncoaHistory.MoveDistance.HasValue && minMoveDistance.HasValue && ncoaHistory.MoveDistance.Value >= minMoveDistance.Value) { History.HistoryChangeList personChanges; personService.InactivatePerson(fm.Person, inactiveReason, $"Received a Change of Address (NCOA) notice that was for more than {minMoveDistance} miles away.", out personChanges); if (personChanges.Any()) { HistoryService.SaveChanges( rockContext, typeof(Person), SystemGuid.Category.HISTORY_PERSON_DEMOGRAPHIC_CHANGES.AsGuid(), fm.PersonId, personChanges, false); } } HistoryService.SaveChanges( rockContext, typeof(Person), SystemGuid.Category.HISTORY_PERSON_FAMILY_CHANGES.AsGuid(), fm.PersonId, familyChanges, family.Name, typeof(Group), family.Id, false); } } } } } rockContext.SaveChanges(); } } } }
/// <summary> /// Starts the NCOA request: Check if there is a valid credit card on the Spark Data server. If there is a valid credit card, then get the addresses, initialize a report on NCOA, upload the addresses to NCOA and delete the previous NcoaHistory data. /// </summary> /// <param name="sparkDataConfig">The spark data configuration.</param> public void Start(SparkDataConfig sparkDataConfig) { if (sparkDataConfig == null) { sparkDataConfig = GetSettings(); } SparkDataApi.SparkDataApi sparkDataApi = new SparkDataApi.SparkDataApi(); var accountStatus = sparkDataApi.CheckAccount(sparkDataConfig.SparkDataApiKey); switch (accountStatus) { case SparkDataApi.SparkDataApi.AccountStatus.AccountNoName: throw new UnauthorizedAccessException("Account does not have a name."); case SparkDataApi.SparkDataApi.AccountStatus.AccountNotFound: throw new UnauthorizedAccessException("Account not found."); case SparkDataApi.SparkDataApi.AccountStatus.Disabled: throw new UnauthorizedAccessException("Account is disabled."); case SparkDataApi.SparkDataApi.AccountStatus.EnabledCardExpired: throw new UnauthorizedAccessException("Credit card on Spark server expired."); case SparkDataApi.SparkDataApi.AccountStatus.EnabledNoCard: throw new UnauthorizedAccessException("No credit card found on Spark server."); case SparkDataApi.SparkDataApi.AccountStatus.InvalidSparkDataKey: throw new UnauthorizedAccessException("Invalid Spark Data Key."); } var addresses = GetAddresses(sparkDataConfig.NcoaSettings.PersonDataViewId); if (addresses.Count == 0) { sparkDataConfig.NcoaSettings.LastRunDate = RockDateTime.Now; sparkDataConfig.NcoaSettings.CurrentReportStatus = "Complete"; SaveSettings(sparkDataConfig); return; } GroupNameTransactionKey groupNameTransactionKey = null; if (sparkDataConfig.NcoaSettings.FileName.IsNotNullOrWhiteSpace()) { groupNameTransactionKey = sparkDataApi.NcoaRetryReport(sparkDataConfig.SparkDataApiKey, sparkDataConfig.NcoaSettings.FileName); } if (groupNameTransactionKey == null) { groupNameTransactionKey = sparkDataApi.NcoaInitiateReport(sparkDataConfig.SparkDataApiKey, addresses.Count, sparkDataConfig.NcoaSettings.PersonFullName); } if (groupNameTransactionKey == null) { if (sparkDataConfig.NcoaSettings.CurrentReportStatus == "Failed") { // To avoid trying to charging customer over and over again... sparkDataConfig.NcoaSettings.IsEnabled = false; } throw new Exception("Init NCOA: Failed to initialize request."); } sparkDataConfig.NcoaSettings.FileName = groupNameTransactionKey.TransactionKey; var credentials = sparkDataApi.NcoaGetCredentials(sparkDataConfig.SparkDataApiKey); var ncoaApi = new NcoaApi(credentials); string id; ncoaApi.CreateFile(sparkDataConfig.NcoaSettings.FileName, groupNameTransactionKey.GroupName, out id); sparkDataConfig.NcoaSettings.CurrentReportKey = id; ncoaApi.UploadAddresses(addresses, sparkDataConfig.NcoaSettings.CurrentReportKey); sparkDataConfig.NcoaSettings.CurrentUploadCount = addresses.Count; ncoaApi.CreateReport(sparkDataConfig.NcoaSettings.CurrentReportKey); // Delete previous NcoaHistory entries. This prevent an user thinking the previous run's data is the current run's data. using (RockContext rockContext = new RockContext()) { NcoaHistoryService ncoaHistoryService = new NcoaHistoryService(rockContext); ncoaHistoryService.DeleteRange(ncoaHistoryService.Queryable()); rockContext.SaveChanges(); } sparkDataConfig.NcoaSettings.CurrentReportStatus = "Pending: Report"; SaveSettings(sparkDataConfig); }