public async Task Execute(Guid runId) { var runCompleted = false; try { runCompleted = await WaitForRunCompletedAsync(runId).ConfigureAwait(false); } catch (Exception ex) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForException(0, 0, $"Completed run notification error (RunID={runId})", ex)); } finally { if (runCompleted) { _runManager.CreateNotificationForCompletedRun(_run); _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Completed run notification has sent (RunID={runId})")); } else { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, $"Completed run notification warning, Run not found (RunID={runId})")); } } }
public ProcessBreakEfficiencyOutput ProcessFile(Guid scenarioId, string folder) { var output = new ProcessBreakEfficiencyOutput(); var run = _dataSnapshot.Run.Value; var efficiencySettings = _efficiencySettingsRepository.GetDefault(); if (!run.Manual || run.Manual && efficiencySettings.PersistEfficiency == PersistEfficiency.AllRun) { if (scenarioId == run.Scenarios.OrderBy(c => c.Id).First().Id) { try { output = ProcessFileInternal(scenarioId, folder); } catch (Exception exception) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForException(0, 0, $"Error processing Break Efficiency", exception)); } } else { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, $"Ignoring break efficiency because it should only be processed once for the run")); } } return(output); }
public IEnumerable <ConversionEfficiency> Process(string pathToFile) { if (!File.Exists(pathToFile)) { _audit?.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, "File was not found.")); return(Enumerable.Empty <ConversionEfficiency>()); } _audit?.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processing output file {pathToFile}")); try { var importSettings = CSVImportSettings.GetImportSettings(pathToFile, typeof(ConversionEfficiencyHeaderMap), typeof(ConversionEfficiencyIndexMap)); var conversionEfficiency = new CSVConversionEfficiencyImportRepository(importSettings); var data = conversionEfficiency.GetAll(); var output = _mapper.Map <IEnumerable <ConversionEfficiency> >(data); _audit?.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processed output file {pathToFile}")); return(output); } catch (Exception exception) { _audit?.Insert(AuditEventFactory.CreateAuditEventForException(0, 0, $"Error processing file {pathToFile}", exception)); return(Enumerable.Empty <ConversionEfficiency>()); } }
/// <summary> /// Load breaks for break efficiency import record if not already in memory /// </summary> /// <param name="salesArea"></param> /// <param name="breakEfficiencyImport"></param> /// <param name="schedules"></param> private void LoadBreaks(SalesArea salesArea, BreakEfficiencyImport breakEfficiencyImport, List <ScheduleIndexed <Break, int> > schedules, IScheduleRepository scheduleRepository) { DateTime startDateTime = GetStartDateTime(breakEfficiencyImport); // Load Schedule document if necessary var schedule = schedules.Where(s => s.SalesArea == salesArea.Name && s.Date.Date == startDateTime.Date).FirstOrDefault(); if (schedule == null) // Not in memory, load it { var scheduleObject = scheduleRepository.GetSchedule(salesArea.Name, startDateTime.Date); if (scheduleObject != null) { if (scheduleObject.Breaks != null && scheduleObject.Breaks.Count > 0) // Schedule has breaks { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, string.Format("Break Efficiency processing. Schedule loaded for sales area {0} and date {1}", salesArea.Name, startDateTime.Date))); scheduleObject.Breaks.ForEach(@break => @break.BreakEfficiencyList = null); } else // No breaks { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, string.Format("Break Efficiency processing. Schedule document has no breaks for sales area {0} and date {1}", salesArea.Name, startDateTime.Date))); } schedules.Add(new ScheduleIndexed <Break, int>(scheduleObject, delegate(Break currentBreak) { return(currentBreak); }, delegate(Break currentBreak) { return(currentBreak.CustomId); })); } else // No Schedule document, log warning { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, string.Format("Break Efficiency processing. Schedule document does not exist for sales area {0} and date {1}", salesArea.Name, startDateTime.Date))); } } }
public CampaignsReqmOutput ProcessFile(Guid scenarioId, string folder) { string pathToFile = FileHelpers.GetPathToFileIfExists(folder, FileName); var result = new CampaignsReqmOutput(); if (String.IsNullOrEmpty(pathToFile)) { _audit.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, $"File {pathToFile} was not found.")); return(result); } _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processing output file {pathToFile}")); var importSettings = CSVImportSettings.GetImportSettings(pathToFile, typeof(CampaignsReqmHeaderMap), typeof(CampaignsReqmIndexMap)); ICampaignsReqmImportRepository _fileReader = new CSVCampaignsReqmImportRepository(importSettings); var data = _fileReader.GetAll().ToList(); result.Data = _mapper.Map <IEnumerable <CampaignsReqm> >(data); _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processed output file {pathToFile}")); return(result); }
public ScenarioCampaignLevelResult ProcessFile(Guid scenarioId, string folder) { string pathToFile = FileHelpers.GetPathToFileIfExists(folder, FileName); var result = new ScenarioCampaignLevelResult { Id = scenarioId }; if (string.IsNullOrEmpty(pathToFile)) { _audit.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, $"File {pathToFile} was not found.")); return(result); } _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processing output file {pathToFile}")); var importSettings = CSVImportSettings.GetImportSettings(pathToFile, typeof(ScenarioCampaignLevelResultHeaderMap), typeof(ScenarioCampaignLevelResultIndexMap)); IScenarioCampaignLevelResultsImportRepository repository = new CSVScenarioCampaignLevelResultsImportRepository(importSettings); var data = repository.GetAll(); result.Items = _mapper.Map <List <ScenarioCampaignLevelResultItem> >(data); _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processed output file {pathToFile}")); return(result); }
public Failures ProcessFile(Guid scenarioId, string folder) { string pathToFile = FileHelpers.GetPathToFileIfExists(folder, FileName); var failures = new Failures { Id = scenarioId }; if (string.IsNullOrEmpty(pathToFile)) { _audit.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, $"File {pathToFile} was not found.")); return(failures); } _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processing output file {pathToFile}")); var importSettings = CSVImportSettings.GetImportSettings(pathToFile, typeof(FailureHeaderMap), typeof(FailureIndexMap)); var campaignsIndex = _dataSnapshot.AllCampaigns.Value.ToDictionary(c => (long)c.CustomId); var salesAreaIndex = _dataSnapshot.AllSalesAreas.Value.ToDictionary(c => c.CustomId); IFailureImportRepository failureRepository = new CSVFailureImportRepository(importSettings); foreach (var summary in failureRepository.GetAll()) { var failure = new Failure(); if (!campaignsIndex.TryGetValue(summary.Campaign, out var campaign)) { failure.ExternalId = failure.CampaignName = "Unknown"; } else { failure.ExternalId = campaign.ExternalId; failure.CampaignName = campaign.Name; } failure.Type = summary.Type; failure.Failures = summary.Failures; failure.Campaign = summary.Campaign; failure.SalesAreaName = !salesAreaIndex.TryGetValue(summary.SalesAreaNumberOfBooking, out var salesArea) ? "Unknown" : salesArea.Name; failures.Items.Add(failure); } _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processed output file {pathToFile}")); return(failures); }
public BaseRatingsOutput ProcessFile(Guid scenarioId, string folder) { string pathToFile = FileHelpers.GetPathToFileIfExists(folder, FileName); var result = new BaseRatingsOutput(); if (String.IsNullOrEmpty(pathToFile)) { _audit.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, $"File {pathToFile} was not found.")); return(result); } _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processing output file {pathToFile}")); try { var importSettings = CSVImportSettings.GetImportSettings(pathToFile, typeof(BaseRatingsHeaderMap), typeof(BaseRatingsIndexMap)); var baseRatingsImportRepository = new CSVBaseRatingsImportRepository(importSettings); var data = baseRatingsImportRepository.GetAll(); result.Data = _mapper.Map <IEnumerable <BaseRatings> >(data); _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processed output file {pathToFile}")); return(result); } catch (Exception exception) { _audit.Insert(AuditEventFactory.CreateAuditEventForException(0, 0, "Error processing base ratings file", exception)); return(result); } }
public SystemLogicalDateService(ITenantSettingsRepository tenantSettingsRepository, IFeatureManager featureManager, IClock clock, IAuditEventRepository auditEventRepository) { _clock = clock; var tenantSettings = tenantSettingsRepository.Get(); var useSystemLogicalDate = featureManager.IsEnabled(nameof(ProductFeature.UseSystemLogicalDate)); if (useSystemLogicalDate) { var isDateValid = DateTime.TryParseExact(tenantSettings.SystemLogicalDate, SystemLogicalDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var systemLogicalDate); if (isDateValid) { _isSystemLogicalDateUsed = true; _systemLogicalDate = systemLogicalDate.Add(_clock.GetCurrentInstant().ToDateTimeUtc().TimeOfDay); } else { auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, $"System Logical Date is not it valid format. " + $"Date set: '{tenantSettings.SystemLogicalDate}', but valid format is: '{SystemLogicalDateFormat}'. Using current Date instead.")); } } }
public ScenarioCampaignResult ProcessFile(Guid scenarioId, string folder) { string pathToFile = FileHelpers.GetPathToFileIfExists(folder, FileName); var result = new ScenarioCampaignResult { Id = scenarioId }; if (string.IsNullOrEmpty(pathToFile)) { _audit.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, $"File {pathToFile} was not found.")); return(result); } _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processing output file {pathToFile}")); var importSettings = CSVImportSettings.GetImportSettings(pathToFile, typeof(ScenarioCampaignResultHeaderMap), typeof(ScenarioCampaignResultIndexMap)); IScenarioCampaignResultsImportRepository repository = new CSVScenarioCampaignResultsImportRepository(importSettings); var data = repository.GetAll(); var salesAreaIndex = _dataSnapshot.AllSalesAreas.Value.ToDictionary(c => c.CustomId); foreach (var importItem in data) { salesAreaIndex.TryGetValue(importItem.SalesAreaNo, out var salesArea); result.Items.Add(MapImportItem(importItem, salesArea)); } _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processed output file {pathToFile}")); return(result); }
public void Execute( Run run, RunScenario scenario, IReadOnlyCollection <AutoBookInstanceConfiguration> autoBookInstanceConfigurationsForRun, double autoBookRequiredStorageGB, ConcurrentBag <RunInstance> runInstances, ConcurrentDictionary <Guid, ScenarioStatuses> newScenarioStatuses, ConcurrentDictionary <Guid, bool> scenarioSyncStatuses, bool autoDistributed) { AutoBookDomainObject autoBook = null; IAutoBook autoBookInterface = null; bool runStarted = false; RaiseInfo($"Begin Execute for ScenarioID: { scenario.Id}"); try { AutoBookInstanceConfiguration runAutoBookInstanceConfiguration = null; if (autoDistributed) { RaiseInfo($"AutoDistributed - RunScenarioTask Execute Starting ScenarioID ={ scenario.Id}, RunID ={ run.Id}"); //create instance for scenario RunInstance runInstance = _runInstanceCreator.Create(run.Id, scenario.Id); RaiseInfo($"AutoDistributed - about to enter: {nameof(runInstance.UploadInputFilesAndCreateAutoBookRequest)}"); runInstance.UploadInputFilesAndCreateAutoBookRequest(autoBookInstanceConfigurationsForRun, autoBookRequiredStorageGB); RaiseInfo($"AutoDistributed - returned from: {nameof(runInstance.UploadInputFilesAndCreateAutoBookRequest)}"); // Flag run as started runStarted = true; runInstances.Add(runInstance); _ = newScenarioStatuses.TryRemove(scenario.Id, out _); // Don't update scenario status at the end scenarioSyncStatuses[scenario.Id] = false; RaiseInfo($"AutoDistributed - RunScenarioTask Execute Started ScenarioID ={ scenario.Id}, RunID ={ run.Id}"); } else { try { using (MachineLock.Create("xggameplan.AWSAutoBooks.GetFreeAutoBook", new TimeSpan(0, 10, 0))) { foreach (var autoBookInstanceConfiguration in autoBookInstanceConfigurationsForRun) { autoBook = _autoBooks.GetFirstAdequateIdleAutoBook(autoBookInstanceConfiguration, autoBookRequiredStorageGB, true); if (autoBook != null) // Got free AutoBook { RaiseInfo($"Got Free AutoBook: {autoBook.Id} ConfigurationId: {autoBook.InstanceConfigurationId}"); runAutoBookInstanceConfiguration = autoBookInstanceConfiguration; break; } } } } catch (MachineLockTimeoutException) { RaiseInfo($"MachineLockTimeoutException in xggameplan.AWSAutoBooks.GetFreeAutoBook"); } // Get autobook interface autoBookInterface = (autoBook == null) ? null : _autoBooks.GetInterface(autoBook); // Get free AutoBook instance, will be locked so that it can't be used elsewhere if (autoBook != null) // Free AutoBook - start run { RaiseInfo($"Free Autobook - Starting ScenarioID ={ scenario.Id}, AutoBookID ={autoBook?.Id}, RunID ={ run.Id}, Instance Configuration = { runAutoBookInstanceConfiguration.Description }"); // Start run, exception will cause cleanup below RunInstance runInstance = _runInstanceCreator.Create(run.Id, scenario.Id); runInstance.UploadInputFilesStartAutoBookRun(autoBookInterface, autoBook); // Flag run as started runStarted = true; runInstances.Add(runInstance); _ = newScenarioStatuses.TryRemove(scenario.Id, out _); // Don't update scenario status at the end scenarioSyncStatuses[scenario.Id] = false; RaiseInfo($"Started ScenarioID ={ scenario.Id}, AutoBookID ={ autoBook?.Id}, RunID ={ run.Id}, Instance Configuration = { runAutoBookInstanceConfiguration?.Description }"); } else // No free AutoBook, awaiting for provisioning { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, $"No free AutoBook, awaiting for provisioning, waiting for existing AutoBooks to be Idle (RunID={run.Id}, ScenarioID={scenario.Id})")); // Update scenario so that it can be retried later when an AutoBook becomes idle RunManager.UpdateScenarioStatuses(_repositoryFactory, _auditEventRepository, run.Id, new List <Guid> { scenario.Id }, new List <ScenarioStatuses> { ScenarioStatuses.Scheduled }, new List <DateTime?> { null }); _ = newScenarioStatuses.TryRemove(scenario.Id, out _); // Don't update scenario status at the end scenarioSyncStatuses[scenario.Id] = false; } } } catch (System.Exception exception) { // Log exception but don't throw it. We want to try and start other scenarios _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForException(0, 0, $"Error starting scenario (RunID={run.Id}, ScenarioID={scenario.Id}, AutoBookID={(autoBook == null ? "Unknown" : autoBook.Id)})", exception)); } finally { // If we locked a free AutoBook instance but didn't start the scenario then reset to free, unlocks it. if (!runStarted && autoBook != null) { autoBookInterface.ResetFree(); } } }
private ProcessBreakEfficiencyOutput ProcessFileInternal(Guid scenarioId, string folder) { var output = new ProcessBreakEfficiencyOutput(); string breakEfficiencyFile = FileHelpers.GetPathToFileIfExists(folder, FileName); if (String.IsNullOrEmpty(breakEfficiencyFile)) { return(output); } _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processing output file {breakEfficiencyFile}")); var demographicsById = Demographic.IndexListById(_dataSnapshot.AllDemographics.Value); var salesAreasIndexed = SalesArea.IndexListByCustomId(_dataSnapshot.AllSalesAreas.Value); // Create memory cache of Schedule documents var schedules = new List <ScheduleIndexed <Break, int> >(); string lastScheduleDateKey = ""; int count = 0; int countUpdated = 0; int countNotUpdated = 0; DateTime nextYield = DateTime.MinValue; // Process CSV in order of sales area + schedule date + schedule // time, allows us to keep as few Break/Schedule documents in memory var importSettings = CSVImportSettings.GetImportSettings(breakEfficiencyFile, typeof(BreakEfficiencyHeaderMap), typeof(BreakEfficiencyIndexMap)); IBreakEfficiencyImportRepository breakEfficiencyImportRepository = new CSVBreakEfficiencyImportRepository(importSettings); IScheduleRepository scheduleRepository = null; var lazyScopeCreator = new Func <Lazy <IRepositoryScope> >(() => new Lazy <IRepositoryScope>(() => _repositoryFactory.BeginRepositoryScope())); var lazyScope = lazyScopeCreator(); try { foreach (BreakEfficiencyImport breakEfficiencyImport in breakEfficiencyImportRepository.GetAll() .OrderBy(be => be.sare_no).ThenBy(be => be.brek_sched_date).ThenBy(be => be.brek_nom_time)) { count++; bool breakUpdated = false; string scheduleDateKey = string.Format("{0}{1}{2}", breakEfficiencyImport.sare_no, (Char)9, breakEfficiencyImport.brek_sched_date); try { // Save changes if different sales area + schedule date if (!String.IsNullOrEmpty(lastScheduleDateKey) && scheduleDateKey != lastScheduleDateKey) // No longer need previous Break/Schedule docs { if (scheduleRepository != null) { schedules.ForEach(scheduleIndexed => scheduleRepository.Add(scheduleIndexed.Schedule)); scheduleRepository.SaveChanges(); } scheduleRepository = null; lazyScope.Value.Dispose(); lazyScope = lazyScopeCreator(); // Clear data that we no longer need schedules.Clear(); } if (scheduleRepository == null) { scheduleRepository = lazyScope.Value.CreateRepository <IScheduleRepository>(); } SalesArea salesArea = salesAreasIndexed.ContainsKey(breakEfficiencyImport.sare_no) ? salesAreasIndexed[breakEfficiencyImport.sare_no] : null; // Load breaks if necessary LoadBreaks(salesArea, breakEfficiencyImport, schedules, scheduleRepository); // Update break efficiency if (UpdateBreakEfficiency(salesArea, breakEfficiencyImport, schedules, demographicsById)) { breakUpdated = true; } } catch (System.Exception exception) { // Log exception, continue _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForException(0, 0, string.Format("Error processing break efficiency for break no {0} (Record {1})", breakEfficiencyImport.break_no, count), exception)); } finally { lastScheduleDateKey = scheduleDateKey; if (breakUpdated) { countUpdated++; } else { countNotUpdated++; } DoYield(false, ref nextYield); } } // Save changed if (scheduleRepository != null) { schedules.ForEach(scheduleIndexed => scheduleRepository.Add(scheduleIndexed.Schedule)); scheduleRepository.SaveChanges(); } } finally { if (scheduleRepository != null) { lazyScope.Value.Dispose(); } } if (countUpdated > 0 && countNotUpdated == 0) // Success, log info event { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, string.Format("Updated {0} break efficiencies (Not updated={1})", countUpdated, countNotUpdated))); } else // Some updates failed, log warning event { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, string.Format("Updated {0} break efficiencies (Not updated={1})", countUpdated, countNotUpdated))); } _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processed output file {breakEfficiencyFile}")); return(output); }
public SpotsReqmOutput ProcessFile(Guid scenarioId, string folder) { string spotFile = FileHelpers.GetPathToFileIfExists(folder, FileName); string mainSpotFile = FileHelpers.GetPathToFileIfExists(folder, OutputFileNames.MainSpotTable); var output = new SpotsReqmOutput { ScenarioId = scenarioId }; if (String.IsNullOrEmpty(spotFile) || String.IsNullOrEmpty(mainSpotFile)) { return(output); } _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processing output file {spotFile}")); _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processing output file {mainSpotFile}")); // Process file DateTime processorDateTime = DateTime.UtcNow; var importSettings = CSVImportSettings.GetImportSettings(mainSpotFile, typeof(SpotHeaderMap), typeof(SpotIndexMap)); // Get main spots if path passed in ISpotImportRepository spotImportRepository = new CSVSpotImportRepository(importSettings); IEnumerable <SpotImport> spotImports = spotImportRepository.GetAll(); importSettings = CSVImportSettings.GetImportSettings(spotFile, typeof(SpotReqmHeaderMap), typeof(SpotReqmIndexMap)); ISpotReqmImportRepository spotReqmImportRepository = new CSVSpotReqmImportRepository(importSettings); var spotReqmImports = spotReqmImportRepository.GetAll(); // Index for lookup performance var campaignsByCustomId = CampaignReducedModel.IndexListByCustomId(_campaignRepository.GetAllFlat()); var spotsByCustomId = SpotHelper.IndexListById(_dataSnapshot.SpotsForRun.Value); var demographicsById = Demographic.IndexListById(_dataSnapshot.AllDemographics.Value); var programmeDictionariesById = ProgrammeDictionary.IndexListById(_dataSnapshot.AllProgrammeDictionaries.Value); IEnumerable <Pass> scenarioPasses = _dataSnapshot.ScenarioPasses.Value; foreach (var currentSalesArea in _dataSnapshot.AllSalesAreas.Value) { foreach (SpotReqmImport spotReqmImport in spotReqmImports.Where(sri => sri.sare_no == currentSalesArea.CustomId)) { output.CountTotalSpots++; try { Recommendation recommendation = _mapper.Map <Recommendation>(Tuple.Create(spotReqmImport, new List <ProgrammeDictionary>(), _dataSnapshot.BreakTypes.Value)); recommendation.ScenarioId = scenarioId; recommendation.ProcessorDateTime = processorDateTime; bool isCancelledSpot = !String.IsNullOrEmpty(spotReqmImport.status) && spotReqmImport.status == "C"; if (isCancelledSpot) { output.CountCancelledSpots++; recommendation.SpotSequenceNumber = default; if (spotsByCustomId.TryGetValue((int)spotReqmImport.spot_no, out var spot)) { recommendation.ExternalSpotRef = spot.ExternalSpotRef; recommendation.Sponsored = spot.Sponsored; recommendation.Preemptable = spot.Preemptable; recommendation.Preemptlevel = spot.Preemptlevel; recommendation.MultipartSpot = spot.MultipartSpot; recommendation.MultipartSpotPosition = spot.MultipartSpotPosition; recommendation.MultipartSpotRef = spot.MultipartSpotRef; } else { _audit.Insert( AuditEventFactory.CreateAuditEventForWarningMessage( 0, 0, $"Unable to set cancelled spot details for recommendations" + $" because spot {spotReqmImport.spot_no} does not exist") ); } } else { recommendation.SpotSequenceNumber = spotReqmImport.spot_no; } Pass pass; if (scenarioPasses != null && scenarioPasses.Any() && (pass = scenarioPasses.FirstOrDefault(p => p.Id == spotReqmImport.abdn_no)) != null) { recommendation.PassName = pass.Name; recommendation.OptimiserPassSequenceNumber = spotReqmImport.pass_sequence_no; } recommendation.CampaignPassPriority = spotReqmImport.campaign_pass_priority; recommendation.RankOfEfficiency = spotReqmImport.rank_of_efficiency; recommendation.RankOfCampaign = spotReqmImport.rank_of_campaign; recommendation.CampaignWeighting = spotReqmImport.campaign_weighting; recommendation.SalesArea = recommendation.GroupCode = currentSalesArea.Name; recommendation.NominalPrice = spotReqmImport.nominal_price; recommendation.ExternalCampaignNumber = campaignsByCustomId.TryGetValue((int)spotReqmImport.camp_no, out var campaign) ? campaign.ExternalId : string.Empty; recommendation.Demographic = demographicsById.TryGetValue(spotReqmImport.demo_no, out var demographic) ? demographic.ExternalRef : string.Empty; if (programmeDictionariesById.TryGetValue((int)spotReqmImport.prog_no, out var programmeDictionary)) { recommendation.ExternalProgrammeReference = programmeDictionary.ExternalReference; recommendation.ProgrammeName = programmeDictionary.ProgrammeName; } else { recommendation.ExternalProgrammeReference = recommendation.ProgrammeName = string.Empty; } recommendation.ClientPicked = spotReqmImport.client_picked.ToUpper() == "Y"; recommendation.ExternalBreakNo = spotReqmImport.brek_external_ref; output.Recommendations.Add(recommendation); } catch (System.Exception exception) { // Log exception, continue _audit.Insert(AuditEventFactory.CreateAuditEventForException(0, 0, string.Format("Error generating recommendation for spot no {0}", spotReqmImport.spot_no), exception)); } } } // TODO: Remove this when it has been confirmed that cancellations appear in Spot Req file // Process cancelled spots if they weren't present in the Spot Req // file above if (output.CountCancelledSpots == 0) { string scheduleIndexKey = string.Empty; var scheduleBreaksBySalesAreaAndDate = _dataSnapshot .BreaksForRun .Value .GroupBy(k => GetScheduleIndexKey(k.SalesAreaName, k.ScheduleDate.Date)) .ToDictionary(c => c.Key, c => c.ToDictionary(v => v.CustomId)); _audit.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, string.Format("Processing {0} for cancellations because no cancelled spots were found in {1}", Path.GetFileName(mainSpotFile), Path.GetFileName(spotFile)))); foreach (var currentSalesArea in _dataSnapshot.AllSalesAreas.Value) { foreach (SpotImport spotImport2 in spotImports.Where(s => s.status == "C" && s.sare_no == currentSalesArea.CustomId)) { try { scheduleIndexKey = string.Empty; bool isCancelledSpot = !String.IsNullOrEmpty(spotImport2.status) && spotImport2.status == "C"; // Should all be cancelled // Update statistics for Spot Performance, Campaign // Performance, New Efficiency, cancelled spots // should be excluded output.CountTotalSpots++; if (isCancelledSpot) { output.CountCancelledSpots++; } // Create recommendation Recommendation recommendation = _mapper.Map <Recommendation>(Tuple.Create(spotImport2, new List <ProgrammeDictionary>())); recommendation.ScenarioId = scenarioId; recommendation.ProcessorDateTime = processorDateTime; if (spotsByCustomId.TryGetValue((int)spotImport2.spot_no, out var spot)) { recommendation.Sponsored = spot.Sponsored; recommendation.Preemptable = spot.Preemptable; recommendation.Preemptlevel = spot.Preemptlevel; recommendation.ExternalSpotRef = spot.ExternalSpotRef; recommendation.MultipartSpot = spot.MultipartSpot; recommendation.MultipartSpotPosition = spot.MultipartSpotPosition; recommendation.MultipartSpotRef = spot.MultipartSpotRef; } else { _audit.Insert( AuditEventFactory.CreateAuditEventForWarningMessage( 0, 0, $"Unable to set multipart spot details for recommendations because spot {spotImport2.spot_no} does not exist" ) ); } recommendation.SalesArea = recommendation.GroupCode = currentSalesArea.Name; if (campaignsByCustomId.TryGetValue((int)spotImport2.camp_no, out CampaignReducedModel campaign)) { recommendation.ExternalCampaignNumber = campaign.ExternalId; } if (demographicsById.TryGetValue(spotImport2.demo_no, out Demographic demographic)) { recommendation.Demographic = demographic.ExternalRef; } scheduleIndexKey = GetScheduleIndexKey(recommendation.SalesArea, recommendation.StartDateTime.Date); string breakNotSetReason = "Unknown reason"; if (spotImport2 == null) { breakNotSetReason = $"Spot not found in {Path.GetFileName(mainSpotFile)}"; } else { if (!scheduleBreaksBySalesAreaAndDate.TryGetValue(scheduleIndexKey, out var scheduleBreakCache)) { breakNotSetReason = string.Format("Schedule document not found (SalesArea={0}, StartDateTime={1})", recommendation.SalesArea, recommendation.StartDateTime.ToString()); } else { recommendation.ExternalBreakNo = !scheduleBreakCache.TryGetValue(spotImport2.break_no, out var @break) ? recommendation.ExternalBreakNo : @break.ExternalBreakRef; // Set reason why ExternalBreakNo isn't set if (String.IsNullOrEmpty(recommendation.ExternalBreakNo)) { if (!(scheduleBreakCache?.Any() ?? false)) { breakNotSetReason = $"Schedule document exists but it contains no breakss (BreakNo={spotImport2.break_no}, Breaks=0)"; } else if (@break == null) { breakNotSetReason = $"Schedule document exists but the break was not found (BreakNo={spotImport2.break_no}, Breaks={scheduleBreakCache.Count})"; } else // Break found but ExternalBreakRef is not set { breakNotSetReason = $"Schedule document exists and the break was found but the ExternalBreakRef is not set (BreakNo={spotImport2.break_no}, Breaks={scheduleBreakCache.Count})"; } } } recommendation.ClientPicked = (spotImport2.client_picked.ToUpper() == "Y"); } if (String.IsNullOrEmpty(recommendation.ExternalBreakNo)) { _audit.Insert( AuditEventFactory.CreateAuditEventForWarningMessage( 0, 0, $"Unable to determine External Break No for recommendation for spot no {spotImport2.spot_no}: {breakNotSetReason}" ) ); } output.Recommendations.Add(recommendation); } catch (Exception exception) { // Log exception, continue _audit.Insert(AuditEventFactory.CreateAuditEventForException(0, 0, string.Format("Error generating cancellation recommendation for spot no {0}", spotImport2.spot_no), exception)); } } } } _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processed output file {spotFile}")); _audit.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, $"Processed output file {mainSpotFile}")); return(output); }
/// <summary> /// Handles run completed, all scenarios /// </summary> /// <param name="run"></param> /// <param name="success"></param> public void Notify(Run run, bool success) { TenantSettings tenantSettings = _tenantSettingsRepository.Get(); bool notificationsConfigured = false; _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForRunCompleted(0, 0, run.Id, success ? "Completed successfully" : "Completed with errors")); // Get run event settings if (tenantSettings.RunEventSettings != null) { RunEventSettings runEventSettings = tenantSettings.RunEventSettings.Where(item => item.EventType == RunEvents.RunCompleted).FirstOrDefault(); if (runEventSettings != null) { var exceptions = new List <Exception>(); try { // Check HTTP notification INotification <HTTPNotificationSettings> httpNotification = _notifications?.GetNotification <HTTPNotificationSettings>(); if (runEventSettings.HTTP != null && httpNotification != null && runEventSettings.HTTP.Enabled) { notificationsConfigured = true; try { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForGameplanPipelineStart(0, 0, PipelineEventIDs.STARTED_NOTIFYING_MULE_SOFT_API, run.Id, null, null, string.Format("Generating HTTP notification for run completed (RunID={0}, Success={1})", run.Id, success))); _pipelineAuditEventRepository.Add(PipelineEventHelper.CreatePipelineAuditEvent(AuditEventTypes.GamePlanRun, PipelineEventIDs.STARTED_NOTIFYING_MULE_SOFT_API, run.Id, Guid.Empty, null)); httpNotification.RunCompleted(run, success, runEventSettings.HTTP); _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForGameplanPipelineEnd(0, 0, PipelineEventIDs.FINISHED_NOTIFYING_MULE_SOFT_API, run.Id, null, null, string.Format("Generated HTTP notification for run completed (RunID={0}, Success={1})", run.Id, success), null, null)); _pipelineAuditEventRepository.Add(PipelineEventHelper.CreatePipelineAuditEvent(AuditEventTypes.GamePlanRun, PipelineEventIDs.FINISHED_NOTIFYING_MULE_SOFT_API, run.Id, Guid.Empty, null)); } catch (System.Exception exception) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForGameplanPipelineEnd(0, 0, PipelineEventIDs.FINISHED_NOTIFYING_MULE_SOFT_API, run.Id, null, null, string.Format("Failed to generate HTTP notification for run completed (RunID={0}, Success={1})", run.Id, success), exception.Message, exception)); _pipelineAuditEventRepository.Add(PipelineEventHelper.CreatePipelineAuditEvent(AuditEventTypes.GamePlanRun, PipelineEventIDs.FINISHED_NOTIFYING_MULE_SOFT_API, run.Id, Guid.Empty, exception.Message)); throw; } finally { _pipelineAuditEventRepository.SaveChanges(); } } } catch (Exception exception) { exceptions.Add(exception); } if (exceptions.Count == 1) { throw exceptions[0]; } else if (exceptions.Count > 0) { throw new AggregateException(exceptions); } } } // Log warning if notifications not enabled so that there's no dispute over why they weren't generated if (!notificationsConfigured) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, string.Format("Not generating notification for run completed because notifications are not configured or enabled (RunID={0}, Success={1})", run.Id, success))); } }
/// <summary> /// Starts task execution by starting the process with the TaskInstanceID /// </summary> /// <param name="taskInstance"></param> /// <returns></returns> public TaskResult Execute(TaskInstance taskInstance) { //int taskInstanceTimeout = defaultTaskInstanceTimeout; int taskInstanceTimeout; if (!int.TryParse(_configuration["Environment:TaskInstanceTimeout"], out taskInstanceTimeout)) { taskInstanceTimeout = defaultTaskInstanceTimeout; _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, string.Format("Cannot Parse Environment:TaskInstanceTimeout - defaulting to {0} seconds", defaultTaskInstanceTimeout))); } else { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, string.Format("Loaded Environment:TaskInstanceTimeout - setting to {0}", taskInstanceTimeout))); } var waitSpan = TimeSpan.FromSeconds(taskInstanceTimeout); StreamReader errorReader = null; var taskResult = new TaskResult(); try { using var scope = _repositoryFactory.BeginRepositoryScope(); var taskInstanceRepository = scope.CreateRepository <ITaskInstanceRepository>(); if (_auditEventRepository != null) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, string.Format("Need to execute task {0}: {1} (RootFolder={2})", taskInstance.Id, taskInstance.TaskId, _rootFolder))); } // Save task instance taskInstanceRepository.Add(taskInstance); taskInstanceRepository.SaveChanges(); // Start Task Executor process const Char quotes = '"'; var process = new Process(); var processStartInfo = new ProcessStartInfo(); processStartInfo.FileName = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "xggameplan.taskexecutor.exe"); if (!System.IO.File.Exists(processStartInfo.FileName)) // Not found in same folder as this assembly, try [root]\bin { processStartInfo.FileName = string.Format(@"{0}\bin\xggameplan.taskexecutor.exe", _rootFolder); } if (!System.IO.File.Exists(processStartInfo.FileName)) { if (_auditEventRepository != null) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, string.Format("Cannot start process {0} because file does not exist", processStartInfo.FileName))); } throw new System.IO.FileNotFoundException(string.Format("Task Executor executable {0} does not exist", processStartInfo.FileName), processStartInfo.FileName); } processStartInfo.Arguments = string.Format("/TaskInstanceID={1}{0}{1} /RootFolder={1}{2}{1}", taskInstance.Id, quotes, _rootFolder); process.StartInfo = processStartInfo; // Start the process, don't wait for it to complete if (_auditEventRepository != null) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, string.Format("Starting process {0} (Task ID={1})", processStartInfo.FileName, taskInstance.TaskId))); } process.StartInfo.RedirectStandardOutput = true; process.StartInfo.UseShellExecute = false; bool started = process.Start(); errorReader = process.StandardOutput; if (!started) { if (_auditEventRepository != null) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, string.Format("Failed to start process {0}", processStartInfo.FileName))); } string output = errorReader.ReadToEnd(); throw new Exception($"Unable to start Task Executor {processStartInfo.FileName}. Output : {output}"); } // Wait for status to change to indicate that something is happening, process will typically set to InProgress immediately if (_auditEventRepository != null) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, string.Format("Waiting for status change for process {0} (Task ID={1})", processStartInfo.FileName, taskInstance.TaskId))); } taskResult.Status = WaitForStatusChange(taskInstance.Id, process, TaskInstanceStatues.Starting, waitSpan); if (_auditEventRepository != null) { int?exitCode = process.HasExited ? process.ExitCode : (int?)null; _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, string.Format("Waited (upto) {0} for status change for process {1} (Task ID={2}, (TaskInstance ID={3}, Status={4}, ExitCode={5})", waitSpan.ToString(), processStartInfo.FileName, taskInstance.TaskId, taskInstance.Id, taskResult.Status, (exitCode.HasValue ? exitCode.Value.ToString() : "null")))); } // Wait for completion if requested if (_completionTimeout != null && _completionTimeout.TotalMilliseconds > 0 && taskResult.Status == TaskInstanceStatues.InProgress) { if (_auditEventRepository != null) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, string.Format("Waiting for completion for process {0} (Task ID={1})", processStartInfo.FileName, taskInstance.TaskId))); } taskResult.Status = WaitForCompletion(taskInstance.Id, _completionTimeout); if (_auditEventRepository != null) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForInformationMessage(0, 0, string.Format("Waited for completion for process {0} (Task ID={1}, Status={2})", processStartInfo.FileName, taskInstance.TaskId, taskResult.Status))); } } } catch (Exception exception) { if (_auditEventRepository != null) { _auditEventRepository.Insert(AuditEventFactory.CreateAuditEventForWarningMessage(0, 0, string.Format("Error executing task for Task ID {0}: {1}", taskInstance.TaskId, exception.Message))); } string output = errorReader?.ReadToEnd(); taskResult.Status = TaskInstanceStatues.CompletedError; taskResult.Exception = exception; } return(taskResult); }