/// <summary> /// Start shifts sync from Kronos to Shifts. /// </summary> /// <param name="isRequestFromLogicApp">Checks if request is coming from logic app or portal.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> internal async Task ProcessShiftsAsync(string isRequestFromLogicApp) { this.telemetryClient.TrackTrace($"{Resource.ProcessShiftsAsync} starts at: {DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)} for isRequestFromLogicApp: {isRequestFromLogicApp}"); this.utility.SetQuerySpan(Convert.ToBoolean(isRequestFromLogicApp, CultureInfo.InvariantCulture), out string shiftStartDate, out string shiftEndDate); var allRequiredConfigurations = await this.utility.GetAllConfigurationsAsync().ConfigureAwait(false); // Check whether date range are in correct format. var isCorrectDateRange = Utility.CheckDates(shiftStartDate, shiftEndDate); if (allRequiredConfigurations != null && (bool)allRequiredConfigurations?.IsAllSetUpExists && isCorrectDateRange) { // Get the mapped user details from user to user mapping table. var kronosUsers = await this.GetAllMappedUserDetailsAsync(allRequiredConfigurations.WFIId).ConfigureAwait(false); if (kronosUsers.Any()) { var monthPartitions = Utility.GetMonthPartition(shiftStartDate, shiftEndDate); if (monthPartitions.Count > 0) { var processNumberOfUsersInBatch = this.appSettings.ProcessNumberOfUsersInBatch; var userCount = kronosUsers.Count(); int userIteration = Utility.GetIterablesCount(Convert.ToInt32(processNumberOfUsersInBatch, CultureInfo.InvariantCulture), userCount); foreach (var monthPartitionKey in monthPartitions) { string queryStartDate, queryEndDate; Utility.GetNextDateSpan( monthPartitionKey, monthPartitions.FirstOrDefault(), monthPartitions.LastOrDefault(), shiftStartDate, shiftEndDate, out queryStartDate, out queryEndDate); var processUsersInBatchList = new List <App.KronosWfc.Models.ResponseEntities.HyperFind.ResponseHyperFindResult>(); foreach (var item in kronosUsers?.ToList()) { processUsersInBatchList.Add(new App.KronosWfc.Models.ResponseEntities.HyperFind.ResponseHyperFindResult { PersonNumber = item.KronosPersonNumber, }); } var processBatchUsersQueue = new Queue <App.KronosWfc.Models.ResponseEntities.HyperFind.ResponseHyperFindResult>(processUsersInBatchList); var processKronosUsersQueue = new Queue <UserDetailsModel>(kronosUsers); for (int batchedUserCount = 0; batchedUserCount < userIteration; batchedUserCount++) { var processKronosUsersQueueInBatch = processKronosUsersQueue?.Skip(Convert.ToInt32(processNumberOfUsersInBatch, CultureInfo.InvariantCulture) * batchedUserCount).Take(Convert.ToInt32(processNumberOfUsersInBatch, CultureInfo.InvariantCulture)); var processBatchUsersQueueInBatch = processBatchUsersQueue?.Skip(Convert.ToInt32(processNumberOfUsersInBatch, CultureInfo.InvariantCulture) * batchedUserCount).Take(Convert.ToInt32(processNumberOfUsersInBatch, CultureInfo.InvariantCulture)); var lookUpData = await this.shiftMappingEntityProvider.GetAllShiftMappingEntitiesInBatchAsync( processKronosUsersQueueInBatch, monthPartitionKey, queryStartDate, queryEndDate).ConfigureAwait(false); // Get shift response for a batch of users. var shiftsResponse = await this.shiftsActivity.ShowUpcomingShiftsInBatchAsync( new Uri(allRequiredConfigurations.WfmEndPoint), allRequiredConfigurations.KronosSession, DateTime.Now.ToString(queryStartDate, CultureInfo.InvariantCulture), DateTime.Now.ToString(queryEndDate, CultureInfo.InvariantCulture), processBatchUsersQueueInBatch.ToList()).ConfigureAwait(false); // Kronos api returns any shifts that occur in the date span provided. // We want only the entities that started within the query date span. var shifts = ControllerHelper.FilterEntitiesByQueryDateSpan(shiftsResponse?.Schedule?.ScheduleItems?.ScheduleShifts, queryStartDate, queryEndDate); var lookUpEntriesFoundList = new List <TeamsShiftMappingEntity>(); var shiftsNotFoundList = new List <Shift>(); var userModelList = new List <UserDetailsModel>(); var userModelNotFoundList = new List <UserDetailsModel>(); await this.ProcessShiftEntitiesBatchAsync( allRequiredConfigurations, lookUpEntriesFoundList, shiftsNotFoundList, userModelList, userModelNotFoundList, lookUpData, processKronosUsersQueueInBatch, shifts, monthPartitionKey).ConfigureAwait(false); } } } } } else { this.telemetryClient.TrackTrace("SyncShiftsFromKronos - " + Resource.SetUpNotDoneMessage); } this.telemetryClient.TrackTrace($"{Resource.ProcessShiftsAsync} completed at: {DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)}" + " for isRequestFromLogicApp: " + isRequestFromLogicApp); }
/// <summary> /// Get the list of open shift entities from Kronos and pushes to Shifts. /// </summary> /// <param name="isRequestFromLogicApp">Checks if request is coming from logic app or portal.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> internal async Task ProcessOpenShiftsAsync(string isRequestFromLogicApp) { this.telemetryClient.TrackTrace($"{Resource.ProcessOpenShiftsAsync} started at: {DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)} for isRequestFromLogicApp: {isRequestFromLogicApp}"); // Adding the telemetry properties. var telemetryProps = new Dictionary <string, string>() { { "MethodName", Resource.ProcessOpenShiftsAsync }, { "CallingAssembly", Assembly.GetCallingAssembly().GetName().Name }, }; if (isRequestFromLogicApp == null) { throw new ArgumentNullException(nameof(isRequestFromLogicApp)); } var allRequiredConfigurations = await this.utility.GetAllConfigurationsAsync().ConfigureAwait(false); if (allRequiredConfigurations?.IsAllSetUpExists == false) { throw new Exception($"{Resource.SyncOpenShiftsFromKronos} - {Resource.SetUpNotDoneMessage} - Some configuration settings were missing."); } this.utility.SetQuerySpan(Convert.ToBoolean(isRequestFromLogicApp, CultureInfo.InvariantCulture), out var openShiftStartDate, out var openShiftEndDate); // Check whether date range are in correct format. if (!Utility.CheckDates(openShiftStartDate, openShiftEndDate)) { throw new Exception($"{Resource.SyncOpenShiftsFromKronos} - Query date was invalid."); } var monthPartitions = Utility.GetMonthPartition(openShiftStartDate, openShiftEndDate); if (monthPartitions?.Count > 0) { telemetryProps.Add("MonthPartitionsStatus", "There are no month partitions found!"); } var orgJobBatchSize = int.Parse(this.appSettings.ProcessNumberOfOrgJobsInBatch, CultureInfo.InvariantCulture); var orgJobPaths = await this.teamDepartmentMappingProvider.GetAllOrgJobPathsAsync().ConfigureAwait(false); var mappedTeams = await this.teamDepartmentMappingProvider.GetMappedTeamToDeptsWithJobPathsAsync().ConfigureAwait(false); int orgJobPathIterations = Utility.GetIterablesCount(orgJobBatchSize, orgJobPaths.Count); // The monthPartitions is a list of strings which are formatted as: MM_YYYY to allow processing in batches foreach (var monthPartitionKey in monthPartitions) { if (monthPartitionKey == null) { this.telemetryClient.TrackTrace($"{Resource.MonthPartitionKeyStatus} - MonthPartitionKey cannot be found please check the data."); this.telemetryClient.TrackTrace(Resource.SyncOpenShiftsFromKronos, telemetryProps); continue; } this.telemetryClient.TrackTrace($"Processing data for the month partition: {monthPartitionKey} at {DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)}"); Utility.GetNextDateSpan( monthPartitionKey, monthPartitions.FirstOrDefault(), monthPartitions.LastOrDefault(), openShiftStartDate, openShiftEndDate, out string queryStartDate, out string queryEndDate); var orgJobPathList = new List <string>(orgJobPaths); // This for loop will iterate over the batched Org Job Paths. for (int iteration = 0; iteration < orgJobPathIterations; iteration++) { this.telemetryClient.TrackTrace($"OpenShiftController - Processing on iteration number: {iteration}"); var orgJobsInBatch = orgJobPathList?.Skip(orgJobBatchSize * iteration).Take(orgJobBatchSize); // Get the response for a batch of org job paths. var openShiftsResponse = await this.GetOpenShiftResultsByOrgJobPathInBatchAsync( allRequiredConfigurations.WFIId, allRequiredConfigurations.WfmEndPoint, allRequiredConfigurations.KronosSession, orgJobsInBatch.ToList(), queryStartDate, queryEndDate).ConfigureAwait(false); if (openShiftsResponse != null) { foreach (var orgJob in orgJobsInBatch) { this.telemetryClient.TrackTrace($"OpenShiftController - Processing the org job path: {orgJob}"); // Open Shift models for Create/Update operation var lookUpEntriesFoundList = new List <AllOpenShiftMappingEntity>(); var openShiftsFoundList = new List <OpenShiftRequestModel>(); var openShiftsNotFoundList = new List <OpenShiftRequestModel>(); var formattedOrgJob = Utility.OrgJobPathDBConversion(orgJob); var mappedOrgJobEntity = mappedTeams?.FirstOrDefault(x => x.PartitionKey == allRequiredConfigurations.WFIId && x.RowKey == formattedOrgJob); // Retrieve lookUpData for the open shift entity. var lookUpData = await this.openShiftMappingEntityProvider.GetAllOpenShiftMappingEntitiesInBatch( monthPartitionKey, formattedOrgJob, queryStartDate, queryEndDate).ConfigureAwait(false); if (lookUpData != null) { // This foreach loop will process the openShiftSchedule item(s) that belong to a specific Kronos Org Job Path. foreach (var openShiftSchedule in openShiftsResponse?.Schedules.Where(x => x.OrgJobPath == orgJob)) { this.telemetryClient.TrackTrace($"OpenShiftController - Processing the Open Shift schedule for: {openShiftSchedule.OrgJobPath}, and date range: {openShiftSchedule.QueryDateSpan}"); // Kronos api returns any open shifts that occur in the date span provided. // We want only the entities that started within the query date span. var openShifts = ControllerHelper.FilterEntitiesByQueryDateSpan(openShiftSchedule.ScheduleItems?.ScheduleShifts, queryStartDate, queryEndDate); if (mappedOrgJobEntity == null) { this.telemetryClient.TrackTrace($"{Resource.SyncOpenShiftsFromKronos} - There is no mappedTeam found with WFI ID: {allRequiredConfigurations.WFIId}"); continue; } if (openShifts.Count == 0) { this.telemetryClient.TrackTrace($"OpenShiftCount - {openShifts.Count} for {openShiftSchedule.OrgJobPath}"); continue; } this.telemetryClient.TrackTrace($"OpenShiftController - Processing Open Shifts for the mapped team: {mappedOrgJobEntity.ShiftsTeamName}"); // This foreach builds the Open Shift object to push to Shifts via Graph API. foreach (var openShift in openShifts) { this.telemetryClient.TrackTrace($"OpenShiftController - Processing {openShift.StartDate} with {openShift.ShiftSegments.ShiftSegment.Count} segments."); var teamsOpenShiftEntity = this.GenerateTeamsOpenShiftEntity(openShift, mappedOrgJobEntity); if (lookUpData.Count == 0) { this.telemetryClient.TrackTrace($"OpenShiftController - Adding {teamsOpenShiftEntity.KronosUniqueId} to the openShiftsNotFoundList as the lookUpData count = 0"); openShiftsNotFoundList.Add(teamsOpenShiftEntity); } else { var kronosUniqueIdExists = lookUpData.Where(c => c.KronosOpenShiftUniqueId == teamsOpenShiftEntity.KronosUniqueId); if ((kronosUniqueIdExists != default(List <AllOpenShiftMappingEntity>)) && kronosUniqueIdExists.Any()) { this.telemetryClient.TrackTrace($"OpenShiftController - Adding {kronosUniqueIdExists.FirstOrDefault().KronosOpenShiftUniqueId} to the lookUpEntriesFoundList as there is data in the lookUpData list."); lookUpEntriesFoundList.AddRange(kronosUniqueIdExists); openShiftsFoundList.Add(teamsOpenShiftEntity); } else { this.telemetryClient.TrackTrace($"OpenShiftController - Adding {teamsOpenShiftEntity.KronosUniqueId} to the openShiftsNotFoundList."); openShiftsNotFoundList.Add(teamsOpenShiftEntity); } } } } // We now want to process open shifts that are identical to one or more other open shifts, // this includes open shifts with a slot count in Teams as well as any open shift with a matching hash. await this.ProcessIdenticalOpenShifts(allRequiredConfigurations, monthPartitionKey, openShiftsFoundList, lookUpEntriesFoundList, mappedOrgJobEntity, lookUpData).ConfigureAwait(false); if (lookUpData.Except(lookUpEntriesFoundList).Any()) { this.telemetryClient.TrackTrace($"OpenShiftController - The lookUpEntriesFoundList has {lookUpEntriesFoundList.Count} items which could be deleted"); await this.DeleteOrphanDataOpenShiftsEntityMappingAsync(allRequiredConfigurations, lookUpEntriesFoundList, lookUpData, mappedOrgJobEntity).ConfigureAwait(false); } if (openShiftsNotFoundList.Count > 0) { this.telemetryClient.TrackTrace($"OpenShiftController - The openShiftsNotFoundList has {openShiftsNotFoundList.Count} items which could be added."); await this.CreateEntryOpenShiftsEntityMappingAsync(allRequiredConfigurations, openShiftsNotFoundList, lookUpEntriesFoundList, monthPartitionKey, mappedOrgJobEntity).ConfigureAwait(false); } } else { this.telemetryClient.TrackTrace($"{Resource.SyncOpenShiftsFromKronos} - There is no lookup data present with the schedulingGroupId: " + mappedOrgJobEntity?.TeamsScheduleGroupId); continue; } } } } } this.telemetryClient.TrackTrace($"{Resource.ProcessOpenShiftsAsync} ended at: {DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)} for isRequestFromLogicApp: {isRequestFromLogicApp}"); }