public void CalculateProviderResult_WhenAllocationModelThrowsException_ShouldThrowException() { // Arrange CalculationEngine calcEngine = CreateCalculationEngine(); IAllocationModel mockAllocationModel = Substitute.For <IAllocationModel>(); mockAllocationModel .Execute(Arg.Any <List <ProviderSourceDataset> >(), Arg.Any <ProviderSummary>()) .Throws(new DivideByZeroException()); List <CalculationSummaryModel> models = new List <CalculationSummaryModel> { new CalculationSummaryModel() { CalculationType = CalculationType.Number, Name = "Test", Id = "Test" } }; ProviderSummary provider = new ProviderSummary(); List <ProviderSourceDataset> sourceDataset = new List <ProviderSourceDataset>(); // Act Action calculateProviderResultMethod = () => { calcEngine.CalculateProviderResults(mockAllocationModel, CreateBuildProject(), models, provider, sourceDataset); }; // Assert calculateProviderResultMethod .Should() .ThrowExactly <DivideByZeroException>(); }
public async Task <(IEnumerable <CalculationResult>, long)> Execute(string specificationId, string providerId) { AllocationFactory allocationFactory = new AllocationFactory(_logger, _featureToggles); IAllocationModel allocationModel = allocationFactory.CreateAllocationModel(typeof(Calculations).Assembly); IEnumerable <Models.Results.ProviderSourceDataset> providerSourceDatasetsResult = await _providerSourceDatasetsRepository.GetProviderSourceDatasetsByProviderIdsAndSpecificationId(new string[] { providerId }, specificationId); List <ProviderSourceDataset> providerSourceDatasets = new List <ProviderSourceDataset>(providerSourceDatasetsResult); IEnumerable <ProviderSummary> providers = await _providerService.FetchCoreProviderData(); ProviderSummary provider = providers.FirstOrDefault(p => p.Id == providerId); if (provider == null) { throw new InvalidOperationException("Provider not found"); } Stopwatch sw = Stopwatch.StartNew(); IEnumerable <CalculationResult> calculationResults = allocationModel.Execute(providerSourceDatasets, provider, null); sw.Stop(); return(calculationResults, sw.ElapsedMilliseconds); }
public void CalculateProviderResult_WhenCalculationsAreNull_ShouldReturnResultWithEmptyCalculations() { // Arrange IAllocationModel mockAllocationModel = Substitute.For <IAllocationModel>(); mockAllocationModel .Execute( Arg.Any <Dictionary <string, ProviderSourceDataset> >(), Arg.Any <ProviderSummary>(), Arg.Any <IEnumerable <CalculationAggregation> >()) .Returns(new CalculationResultContainer { FundingLineResults = new List <FundingLineResult>() }); CalculationEngine calculationEngine = CreateCalculationEngine(); ProviderSummary providerSummary = CreateDummyProviderSummary(); BuildProject buildProject = CreateBuildProject(); // Act ProviderResult result = calculationEngine.CalculateProviderResults( mockAllocationModel, buildProject.SpecificationId, null, providerSummary, new Dictionary <string, ProviderSourceDataset>()); // Assert result.CalculationResults.Should().BeNull(); result.Provider.Should().Be(providerSummary); result.SpecificationId.Should().BeEquivalentTo(buildProject.SpecificationId); result.Id.Should().BeEquivalentTo(GenerateId(providerSummary.Id, buildProject.SpecificationId)); }
public void GenerateAllocations_GivenModelExecuteThrowsException_ThrowsException() { //Arrange BuildProject buildProject = CreateBuildProject(); buildProject.Build.Assembly = MockData.GetMockAssembly(); IEnumerable <ProviderSummary> providers = new[] { new ProviderSummary { Id = ProviderId, Name = ProviderName } }; IEnumerable <ProviderSourceDataset> datasets = new[] { new ProviderSourceDataset() }; Func <string, string, Task <IEnumerable <ProviderSourceDataset> > > func = (s, p) => { return(Task.FromResult(datasets)); }; IEnumerable <CalculationResult> calculationResults = new[] { new CalculationResult { Calculation = new Reference { Id = CalculationId } } }; IAllocationModel allocationModel = Substitute.For <IAllocationModel>(); allocationModel .When(x => x.Execute(Arg.Any <List <ProviderSourceDataset> >(), Arg.Any <ProviderSummary>())) .Do(x => { throw new Exception(); }); IAllocationFactory allocationFactory = Substitute.For <IAllocationFactory>(); allocationFactory .CreateAllocationModel(Arg.Any <Assembly>()) .Returns(allocationModel); ILogger logger = CreateLogger(); CalculationEngine calculationEngine = CreateCalculationEngine(allocationFactory, logger: logger); //Act Func <Task> test = () => calculationEngine.GenerateAllocations(buildProject, providers, func); //Assert test .Should() .ThrowExactly <Exception>(); }
static IAllocationFactory CreateAllocationFactory(IAllocationModel allocationModel) { IAllocationFactory allocationFactory = Substitute.For <IAllocationFactory>(); allocationFactory .CreateAllocationModel(Arg.Any <Assembly>()) .Returns(allocationModel); return(allocationFactory); }
public async Task PreviewCalculationResult_GivenProviderSummaryNotFound_ReturnsNotFound() { IAllocationModel allocationModel = Substitute.For <IAllocationModel>(); _calculationEngine .GenerateAllocationModel(Arg.Any <Assembly>()) .Returns(allocationModel); IEnumerable <string> dataDefinitionRelationshipIds = new List <string>(); SpecificationSummary specificationSummary = new SpecificationSummary { DataDefinitionRelationshipIds = dataDefinitionRelationshipIds, ProviderVersionId = providerVersionId }; _specificationsApiClient .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new ApiResponse <SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); _providersApiClient .GetProviderByIdFromProviderVersion(Arg.Is(providerVersionId), Arg.Is(providerId)) .Returns(new ApiResponse <ProviderVersionSearchResult>(HttpStatusCode.OK, (ProviderVersionSearchResult)null)); PreviewCalculationRequest previewCalculationRequest = new PreviewCalculationRequest { AssemblyContent = MockData.GetMockAssembly() }; IActionResult actionResult = await _calculationEnginePreviewService.PreviewCalculationResult(specificationId, providerId, previewCalculationRequest); actionResult .Should() .BeOfType <NotFoundResult>(); }
async public Task GenerateAllocations_GivenBuildProject_Runs() { //Arrange BuildProject buildProject = CreateBuildProject(); buildProject.Build.Assembly = MockData.GetMockAssembly(); IEnumerable <ProviderSummary> providers = new[] { new ProviderSummary { Id = ProviderId, Name = ProviderName } }; IEnumerable <ProviderSourceDataset> datasets = new[] { new ProviderSourceDataset() }; Func <string, string, Task <IEnumerable <ProviderSourceDataset> > > func = (s, p) => { return(Task.FromResult(datasets)); }; IEnumerable <CalculationResult> calculationResults = new[] { new CalculationResult { Calculation = new Reference { Id = CalculationId }, } }; IAllocationModel allocationModel = Substitute.For <IAllocationModel>(); allocationModel .Execute(Arg.Any <List <ProviderSourceDataset> >(), Arg.Any <ProviderSummary>()) .Returns(calculationResults); IAllocationFactory allocationFactory = Substitute.For <IAllocationFactory>(); allocationFactory .CreateAllocationModel(Arg.Any <Assembly>()) .Returns(allocationModel); ILogger logger = CreateLogger(); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); List <CalculationSummaryModel> calculations = new List <CalculationSummaryModel>() { new CalculationSummaryModel() { Id = CalculationId, }, new CalculationSummaryModel() { Id = "calc2", }, new CalculationSummaryModel() { Id = "calc3", } }; calculationsRepository .GetCalculationSummariesForSpecification(Arg.Any <string>()) .Returns(calculations); CalculationEngine calculationEngine = CreateCalculationEngine(allocationFactory, calculationsRepository, logger: logger); //Act IEnumerable <ProviderResult> results = await calculationEngine.GenerateAllocations(buildProject, providers, func); //Assert results .Count() .Should() .Be(1); results .First() .CalculationResults .Count .Should() .Be(3); }
public void CalculateProviderResult_WhenCalculationValuesReturnedWithMultipleAllocationLinesAndMixOfValuesAndNulls_ThenAllocationLineValuesAreSetCorrectly() { // Arrange List <Reference> policySpecificationsForFundingCalc = new List <Reference>() { new Reference("Spec1", "SpecOne"), new Reference("Spec2", "SpecTwo") }; List <Reference> policySpecificationsForNumberCalc = new List <Reference>() { new Reference("Spec1", "SpecOne"), }; Reference allocationLine1 = new Reference("allocationLine", "allocation line for Funding Calc and number calc"); Reference allocationLine2 = new Reference("allocationLine2", "Second allocation line"); Reference allocationLine3 = new Reference("allocationLine3", "Allocation line excluded"); CalculationResult calc1 = new CalculationResult() { CalculationType = CalculationType.Funding, Calculation = new Reference("calc1", "Calc 1"), AllocationLine = allocationLine1, CalculationSpecification = new Reference("FSpect", "FundingSpecification"), PolicySpecifications = policySpecificationsForFundingCalc, Value = 10000 }; CalculationResult calc2 = new CalculationResult() { CalculationType = CalculationType.Funding, Calculation = new Reference("calc2", "Calc 2"), AllocationLine = allocationLine1, CalculationSpecification = new Reference("FSpec2", "FundingSpecification2"), PolicySpecifications = policySpecificationsForNumberCalc, Value = 20000 }; CalculationResult calc3 = new CalculationResult() { CalculationType = CalculationType.Funding, Calculation = new Reference("calc3", "Calc 3"), AllocationLine = allocationLine2, CalculationSpecification = new Reference("calc3", "Calc 3"), PolicySpecifications = policySpecificationsForNumberCalc, Value = 67 }; CalculationResult calc4 = new CalculationResult() { CalculationType = CalculationType.Funding, Calculation = new Reference("calc4", "Calc 4"), AllocationLine = allocationLine3, CalculationSpecification = new Reference("calc4", "Calc 4"), PolicySpecifications = policySpecificationsForNumberCalc, Value = null, }; List <CalculationResult> calculationResults = new List <CalculationResult>() { calc1, calc2, calc3, calc4, }; IAllocationModel mockAllocationModel = Substitute.For <IAllocationModel>(); mockAllocationModel .Execute(Arg.Any <List <ProviderSourceDataset> >(), Arg.Any <ProviderSummary>()) .Returns(calculationResults); CalculationEngine calculationEngine = CreateCalculationEngine(); ProviderSummary providerSummary = CreateDummyProviderSummary(); BuildProject buildProject = CreateBuildProject(); IEnumerable <CalculationSummaryModel> calculationSummaryModels = new[] { new CalculationSummaryModel() { Id = "calc1", Name = "Calc 1", CalculationType = CalculationType.Funding }, new CalculationSummaryModel() { Id = "calc2", Name = "Calc 2", CalculationType = CalculationType.Funding }, new CalculationSummaryModel() { Id = "calc3", Name = "Calc 3", CalculationType = CalculationType.Funding }, new CalculationSummaryModel() { Id = "calc4", Name = "Calc 4", CalculationType = CalculationType.Funding }, }; // Act var calculateProviderResults = calculationEngine.CalculateProviderResults(mockAllocationModel, buildProject, calculationSummaryModels, providerSummary, new List <ProviderSourceDataset>()); ProviderResult result = calculateProviderResults; // Assert result .AllocationLineResults .Should() .HaveCount(3); // Values are summed for allocation line result .AllocationLineResults[0] .Value .Should() .Be(30000); result .AllocationLineResults[1] .Value .Should() .Be(67); // All calculations for Allocation Line 3 returned nulls - therefore allocation line value is null result .AllocationLineResults[2] .Value .Should() .Be(null); }
public void CalculateProviderResult_WhenCalculationsAreNotEmpty_ShouldReturnCorrectResult() { // Arrange List <Reference> policySpecificationsForFundingCalc = new List <Reference>() { new Reference("Spec1", "SpecOne"), new Reference("Spec2", "SpecTwo") }; List <Reference> policySpecificationsForNumberCalc = new List <Reference>() { new Reference("Spec1", "SpecOne"), }; Reference allocationLineReturned = new Reference("allocationLine", "allocation line for Funding Calc and number calc"); Reference fundingCalcReference = new Reference("CalcF1", "Funding calc 1"); Reference fundingCalcSpecificationReference = new Reference("FSpect", "FundingSpecification"); Reference numbercalcReference = new Reference("CalcF2", "Funding calc 2"); Reference numbercalcSpecificationReference = new Reference("FSpec2", "FundingSpecification2"); CalculationResult fundingCalcReturned = new CalculationResult() { CalculationType = CalculationType.Funding, Calculation = fundingCalcReference, AllocationLine = allocationLineReturned, CalculationSpecification = fundingCalcSpecificationReference, PolicySpecifications = policySpecificationsForFundingCalc, Value = 10000 }; CalculationResult fundingCalcReturned2 = new CalculationResult() { CalculationType = CalculationType.Funding, Calculation = numbercalcReference, AllocationLine = allocationLineReturned, CalculationSpecification = numbercalcSpecificationReference, PolicySpecifications = policySpecificationsForNumberCalc, Value = 20000 }; List <CalculationResult> calculationResults = new List <CalculationResult>() { fundingCalcReturned, fundingCalcReturned2 }; IAllocationModel mockAllocationModel = Substitute.For <IAllocationModel>(); mockAllocationModel .Execute(Arg.Any <List <ProviderSourceDataset> >(), Arg.Any <ProviderSummary>()) .Returns(calculationResults); CalculationEngine calculationEngine = CreateCalculationEngine(); ProviderSummary providerSummary = CreateDummyProviderSummary(); BuildProject buildProject = CreateBuildProject(); var nonMatchingCalculationModel = new CalculationSummaryModel() { Id = "Non matching calculation", Name = "Non matching calculation", CalculationType = CalculationType.Funding }; IEnumerable <CalculationSummaryModel> calculationSummaryModels = new[] { new CalculationSummaryModel() { Id = fundingCalcReference.Id, Name = fundingCalcReference.Name, CalculationType = CalculationType.Funding }, new CalculationSummaryModel() { Id = numbercalcReference.Id, Name = numbercalcReference.Name, CalculationType = CalculationType.Funding }, nonMatchingCalculationModel }; // Act var calculateProviderResults = calculationEngine.CalculateProviderResults(mockAllocationModel, buildProject, calculationSummaryModels, providerSummary, new List <ProviderSourceDataset>()); ProviderResult result = calculateProviderResults; // Assert result.Provider.Should().Be(providerSummary); result.SpecificationId.Should().BeEquivalentTo(buildProject.SpecificationId); result.Id.Should().BeEquivalentTo(GenerateId(providerSummary.Id, buildProject.SpecificationId)); result.CalculationResults.Should().HaveCount(3); result.AllocationLineResults.Should().HaveCount(1); AllocationLineResult allocationLine = result.AllocationLineResults[0]; allocationLine.Value = 30000; CalculationResult fundingCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == fundingCalcReference.Id); fundingCalcResult.Calculation.Should().BeEquivalentTo(fundingCalcReference); fundingCalcResult.CalculationType.Should().BeEquivalentTo(fundingCalcReturned.CalculationType); fundingCalcResult.AllocationLine.Should().BeEquivalentTo(allocationLineReturned); fundingCalcResult.CalculationSpecification.Should().BeEquivalentTo(fundingCalcSpecificationReference); fundingCalcResult.PolicySpecifications.Should().BeEquivalentTo(policySpecificationsForFundingCalc); fundingCalcResult.Value.Should().Be(fundingCalcReturned.Value.Value); CalculationResult numberCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == numbercalcReference.Id); numberCalcResult.Calculation.Should().BeEquivalentTo(numbercalcReference); numberCalcResult.CalculationType.Should().BeEquivalentTo(fundingCalcReturned2.CalculationType); numberCalcResult.AllocationLine.Should().BeEquivalentTo(allocationLineReturned); numberCalcResult.CalculationSpecification.Should().BeEquivalentTo(numbercalcSpecificationReference); numberCalcResult.PolicySpecifications.Should().BeEquivalentTo(policySpecificationsForNumberCalc); numberCalcResult.Value.Should().Be(fundingCalcReturned2.Value.Value); CalculationResult nonMatchingCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == "Non matching calculation"); nonMatchingCalcResult.Calculation.Should().BeEquivalentTo(new Reference(nonMatchingCalculationModel.Id, nonMatchingCalculationModel.Name)); nonMatchingCalcResult.CalculationType.Should().BeEquivalentTo(nonMatchingCalculationModel.CalculationType); nonMatchingCalcResult.AllocationLine.Should().BeNull(); nonMatchingCalcResult.CalculationSpecification.Should().BeNull(); nonMatchingCalcResult.PolicySpecifications.Should().BeNull(); nonMatchingCalcResult.Value.Should().BeNull(); }
public ProviderResult CalculateProviderResults(IAllocationModel model, BuildProject buildProject, IEnumerable <CalculationSummaryModel> calculations, ProviderSummary provider, IEnumerable <ProviderSourceDataset> providerSourceDatasets, IEnumerable <CalculationAggregation> aggregations = null) { var stopwatch = new Stopwatch(); stopwatch.Start(); IEnumerable <CalculationResult> calculationResults = model.Execute(providerSourceDatasets != null ? providerSourceDatasets.ToList() : new List <ProviderSourceDataset>(), provider, aggregations).ToArray(); var providerCalResults = calculationResults.ToDictionary(x => x.Calculation?.Id); stopwatch.Stop(); if (providerCalResults.Count > 0) { _logger.Debug($"{providerCalResults.Count} calcs in {stopwatch.ElapsedMilliseconds}ms ({stopwatch.ElapsedMilliseconds / providerCalResults.Count: 0.0000}ms)"); } else { _logger.Information("There are no calculations to executed for specification ID {specificationId}", buildProject.SpecificationId); } ProviderResult providerResult = new ProviderResult { Provider = provider, SpecificationId = buildProject.SpecificationId }; byte[] plainTextBytes = System.Text.Encoding.UTF8.GetBytes($"{providerResult.Provider.Id}-{providerResult.SpecificationId}"); providerResult.Id = Convert.ToBase64String(plainTextBytes); List <CalculationResult> results = new List <CalculationResult>(); if (calculations != null) { foreach (CalculationSummaryModel calculation in calculations) { CalculationResult result = new CalculationResult { Calculation = calculation.GetReference(), CalculationType = calculation.CalculationType, Version = calculation.Version }; if (providerCalResults.TryGetValue(calculation.Id, out CalculationResult calculationResult)) { result.CalculationSpecification = calculationResult.CalculationSpecification; if (calculationResult.AllocationLine != null) { result.AllocationLine = calculationResult.AllocationLine; } result.PolicySpecifications = calculationResult.PolicySpecifications; // The default for the calculation is to return Decimal.MinValue - if this is the case, then subsitute a 0 value as the result, instead of the negative number. if (calculationResult.Value != decimal.MinValue) { result.Value = calculationResult.Value; } else { result.Value = 0; } result.ExceptionType = calculationResult.ExceptionType; result.ExceptionMessage = calculationResult.ExceptionMessage; } results.Add(result); } } providerResult.CalculationResults = results.ToList(); providerResult.AllocationLineResults = results.Where(x => x.CalculationType == CalculationType.Funding && x.AllocationLine != null) .GroupBy(x => x.AllocationLine).Select(x => new AllocationLineResult { AllocationLine = x.Key, Value = x.All(v => !v.Value.HasValue) ? (decimal?)null : x.Sum(v => v.Value ?? decimal.Zero) }).ToList(); return(providerResult); }
public async Task PreviewCalculationResult_GivenCachedAggregateValuesExist_CalculateProviderResultsCallReceived() { IAllocationModel allocationModel = Substitute.For <IAllocationModel>(); _calculationEngine .GenerateAllocationModel(Arg.Any <Assembly>()) .Returns(allocationModel); ProviderVersionSearchResult providerVersionSearchResult = new ProviderVersionSearchResult { UKPRN = providerId }; IEnumerable <string> dataDefinitionRelationshipIds = new List <string>(); SpecificationSummary specificationSummary = new SpecificationSummary { DataDefinitionRelationshipIds = dataDefinitionRelationshipIds, ProviderVersionId = providerVersionId }; _specificationsApiClient .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new ApiResponse <SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); _providersApiClient .GetProviderByIdFromProviderVersion(Arg.Is(providerVersionId), Arg.Is(providerId)) .Returns(new ApiResponse <ProviderVersionSearchResult>(HttpStatusCode.OK, providerVersionSearchResult)); CalculationSummaryModel previewCalculationSummaryModel = new CalculationSummaryModel(); IEnumerable <CalculationSummaryModel> calculationSummaryModels = new List <CalculationSummaryModel> { new CalculationSummaryModel(), new CalculationSummaryModel() }; List <CalculationSummaryModel> expectedCalculationSummaryModels = calculationSummaryModels.ToList(); expectedCalculationSummaryModels.Add(previewCalculationSummaryModel); _calculationsRepository .GetCalculationSummariesForSpecification(Arg.Is(specificationId)) .Returns(calculationSummaryModels); Dictionary <string, ProviderSourceDataset> sourceDatasets = new Dictionary <string, ProviderSourceDataset>(); Dictionary <string, Dictionary <string, ProviderSourceDataset> > providerSourceDatasets = new Dictionary <string, Dictionary <string, ProviderSourceDataset> > { { providerId, sourceDatasets } }; _providerSourceDatasetsRepository .GetProviderSourceDatasetsByProviderIdsAndRelationshipIds( Arg.Is(specificationId), Arg.Is <IEnumerable <string> >(_ => _ != null && _.Count() == 1 && _.FirstOrDefault() == providerId), Arg.Is <IEnumerable <string> >(_ => _ != null && _.SequenceEqual(dataDefinitionRelationshipIds))) .Returns(providerSourceDatasets); IEnumerable <CalculationAggregation> calculationAggregations = new List <CalculationAggregation>(); _calculationAggregationService .BuildAggregations(Arg.Is <BuildAggregationRequest>(_ => _ != null && _.SpecificationId == specificationId)) .Returns(calculationAggregations); PreviewCalculationRequest previewCalculationRequest = new PreviewCalculationRequest { AssemblyContent = MockData.GetMockAssembly(), PreviewCalculationSummaryModel = previewCalculationSummaryModel }; IActionResult actionResult = await _calculationEnginePreviewService.PreviewCalculationResult(specificationId, providerId, previewCalculationRequest); _calculationEngine .Received(1) .CalculateProviderResults( Arg.Is(allocationModel), specificationId, Arg.Is <IEnumerable <CalculationSummaryModel> >(_ => _.SequenceEqual(expectedCalculationSummaryModels)), Arg.Is <ProviderSummary>(_ => _.UKPRN == providerId), Arg.Is <Dictionary <string, ProviderSourceDataset> >(_ => _.SequenceEqual(sourceDatasets)), Arg.Is <IEnumerable <CalculationAggregation> >(_ => _.SequenceEqual(calculationAggregations))); }
private async Task <CalculationResultsModel> CalculateResults(string specificationId, IEnumerable <ProviderSummary> summaries, IEnumerable <CalculationSummaryModel> calculations, IEnumerable <CalculationAggregation> aggregations, IEnumerable <string> dataRelationshipIds, byte[] assemblyForSpecification, GenerateAllocationMessageProperties messageProperties, int providerBatchSize, int index) { ConcurrentBag <ProviderResult> providerResults = new ConcurrentBag <ProviderResult>(); Guard.ArgumentNotNull(summaries, nameof(summaries)); IEnumerable <ProviderSummary> partitionedSummaries = summaries.Skip(index).Take(providerBatchSize); IList <string> providerIdList = partitionedSummaries.Select(m => m.Id).ToList(); Stopwatch providerSourceDatasetsStopwatch = Stopwatch.StartNew(); _logger.Information($"Fetching provider sources for specification id {messageProperties.SpecificationId}"); Dictionary <string, Dictionary <string, ProviderSourceDataset> > providerSourceDatasetResult = await _providerSourceDatasetsRepositoryPolicy.ExecuteAsync( () => _providerSourceDatasetsRepository.GetProviderSourceDatasetsByProviderIdsAndRelationshipIds(specificationId, providerIdList, dataRelationshipIds)); providerSourceDatasetsStopwatch.Stop(); _logger.Information($"Fetched provider sources found for specification id {messageProperties.SpecificationId}"); _logger.Information($"Calculating results for specification id {messageProperties.SpecificationId}"); Stopwatch assemblyLoadStopwatch = Stopwatch.StartNew(); Assembly assembly = Assembly.Load(assemblyForSpecification); assemblyLoadStopwatch.Stop(); Stopwatch calculationStopwatch = Stopwatch.StartNew(); List <Task> allTasks = new List <Task>(); SemaphoreSlim throttler = new SemaphoreSlim(_engineSettings.CalculateProviderResultsDegreeOfParallelism); IAllocationModel allocationModel = _calculationEngine.GenerateAllocationModel(assembly); foreach (ProviderSummary provider in partitionedSummaries) { await throttler.WaitAsync(); allTasks.Add( Task.Run(() => { try { if (provider == null) { throw new Exception("Provider summary is null"); } if (providerSourceDatasetResult.AnyWithNullCheck()) { if (!providerSourceDatasetResult.TryGetValue(provider.Id, out Dictionary <string, ProviderSourceDataset> providerDatasets)) { throw new Exception($"Provider source dataset not found for {provider.Id}."); } ProviderResult result = _calculationEngine.CalculateProviderResults(allocationModel, specificationId, calculations, provider, providerDatasets, aggregations); if (result == null) { throw new InvalidOperationException("Null result from Calc Engine CalculateProviderResults"); } providerResults.Add(result); } } finally { throttler.Release(); } return(Task.CompletedTask); })); } await TaskHelper.WhenAllAndThrow(allTasks.ToArray()); calculationStopwatch.Stop(); _logger.Information($"Calculating results complete for specification id {messageProperties.SpecificationId} in {calculationStopwatch.ElapsedMilliseconds}ms"); return(new CalculationResultsModel { ProviderResults = providerResults, PartitionedSummaries = partitionedSummaries, CalculationRunMs = calculationStopwatch.ElapsedMilliseconds, AssemblyLoadMs = assemblyLoadStopwatch.ElapsedMilliseconds, ProviderSourceDatasetsLookupMs = providerSourceDatasetsStopwatch.ElapsedMilliseconds, }); }
public ProviderResult CalculateProviderResults( IAllocationModel model, string specificationId, IEnumerable <CalculationSummaryModel> calculations, ProviderSummary provider, IDictionary <string, ProviderSourceDataset> providerSourceDatasets, IEnumerable <CalculationAggregation> aggregations = null) { var stopwatch = new Stopwatch(); stopwatch.Start(); CalculationResultContainer calculationResultContainer = model.Execute(providerSourceDatasets, provider, aggregations); IEnumerable <CalculationResult> calculationResultItems = calculationResultContainer.CalculationResults; stopwatch.Stop(); IDictionary <string, double> metrics = new Dictionary <string, double>() { { "calculation-provider-calcsMs", stopwatch.ElapsedMilliseconds }, { "calculation-provider-calcsTotal", calculations.AnyWithNullCheck() ? calculations.Count() : 0 }, { "calculation-provider-exceptions", calculationResultItems.AnyWithNullCheck() ? calculationResultItems.Count(c => !string.IsNullOrWhiteSpace(c.ExceptionMessage)) : 0 }, }; _telemetry.TrackEvent("CalculationRunProvider", new Dictionary <string, string>() { { "specificationId", specificationId }, }, metrics ); if (calculationResultItems.AnyWithNullCheck() && calculationResultItems.Count() > 0) { _logger.Verbose($"Processed results for {calculationResultItems.Count()} calcs in {stopwatch.ElapsedMilliseconds}ms ({stopwatch.ElapsedMilliseconds / calculationResultItems.Count(): 0.0000}ms)"); } else { _logger.Information("There are no calculations to executed for specification ID {specificationId}", specificationId); } byte[] plainTextBytes = System.Text.Encoding.UTF8.GetBytes($"{provider.Id}-{specificationId}"); ProviderResult providerResult = new ProviderResult { Id = Convert.ToBase64String(plainTextBytes), Provider = provider, SpecificationId = specificationId }; if (calculationResultItems.AnyWithNullCheck()) { foreach (CalculationResult calcResult in calculationResultItems) { CalculationSummaryModel calculationSummaryModel = calculations.First(c => c.Id == calcResult.Calculation.Id); calcResult.CalculationType = calculationSummaryModel.CalculationType; calcResult.CalculationDataType = calculationSummaryModel.CalculationValueType.ToCalculationDataType(); if (calcResult.CalculationDataType == CalculationDataType.Decimal && Decimal.Equals(decimal.MinValue, calcResult.Value)) { // The default for the calculation is to return Decimal.MinValue - if this is the case, then subsitute a 0 value as the result, instead of the negative number. calcResult.Value = 0; } } } //we need a stable sort of results to enable the cache checks by overall SHA hash on the results json providerResult.CalculationResults = calculationResultContainer.CalculationResults?.OrderBy(_ => _.Calculation.Id).ToList(); providerResult.FundingLineResults = calculationResultContainer.FundingLineResults.OrderBy(_ => _.FundingLine.Id).ToList(); return(providerResult); }
public void CalculateProviderResult_WhenCalculationsAreNotEmpty_ShouldReturnCorrectResult() { // Arrange List <Reference> policySpecificationsForFundingCalc = new List <Reference>() { new Reference("Spec1", "SpecOne"), new Reference("Spec2", "SpecTwo") }; Reference fundingCalcReference = new Reference("CalcF1", "Funding calc 1"); Reference numbercalcReference = new Reference("CalcF2", "Funding calc 2"); Reference booleancalcReference = new Reference("CalcF3", "Funding calc 3"); Reference fundingLineCalcReference = new Reference("FL1", "Funding line calc 1"); CalculationResult fundingCalcReturned = new CalculationResult() { Calculation = fundingCalcReference, Value = 10000 }; CalculationResult fundingCalcReturned2 = new CalculationResult() { Calculation = numbercalcReference, Value = 20000 }; CalculationResult fundingCalcReturned3 = new CalculationResult() { Calculation = booleancalcReference, Value = true }; CalculationResultContainer calculationResultContainer = new CalculationResultContainer(); List <CalculationResult> calculationResults = new List <CalculationResult>() { fundingCalcReturned, fundingCalcReturned2, fundingCalcReturned3 }; calculationResultContainer.CalculationResults = calculationResults; string fundingStreamId = "FS1"; FundingLineResult fundingLineResult = new FundingLineResult { Value = 1000, FundingLine = fundingLineCalcReference, FundingLineFundingStreamId = fundingStreamId }; List <FundingLineResult> fundingLineResults = new List <FundingLineResult> { fundingLineResult }; calculationResultContainer.FundingLineResults = fundingLineResults; IAllocationModel mockAllocationModel = Substitute.For <IAllocationModel>(); mockAllocationModel .Execute(Arg.Any <Dictionary <string, ProviderSourceDataset> >(), Arg.Any <ProviderSummary>()) .Returns(calculationResultContainer); CalculationEngine calculationEngine = CreateCalculationEngine(); ProviderSummary providerSummary = CreateDummyProviderSummary(); BuildProject buildProject = CreateBuildProject(); var nonMatchingCalculationModel = new CalculationSummaryModel() { Id = "Non matching calculation", Name = "Non matching calculation", CalculationType = CalculationType.Template, CalculationValueType = CalculationValueType.Number }; IEnumerable <CalculationSummaryModel> calculationSummaryModels = new[] { new CalculationSummaryModel() { Id = fundingCalcReference.Id, Name = fundingCalcReference.Name, CalculationType = CalculationType.Template, CalculationValueType = CalculationValueType.Number }, new CalculationSummaryModel() { Id = numbercalcReference.Id, Name = numbercalcReference.Name, CalculationType = CalculationType.Template, CalculationValueType = CalculationValueType.Number }, new CalculationSummaryModel() { Id = booleancalcReference.Id, Name = booleancalcReference.Name, CalculationType = CalculationType.Template, CalculationValueType = CalculationValueType.Boolean }, nonMatchingCalculationModel }; // Act var calculateProviderResults = calculationEngine.CalculateProviderResults(mockAllocationModel, buildProject.SpecificationId, calculationSummaryModels, providerSummary, new Dictionary <string, ProviderSourceDataset>()); ProviderResult result = calculateProviderResults; // Assert result.Provider.Should().Be(providerSummary); result.SpecificationId.Should().BeEquivalentTo(buildProject.SpecificationId); result.Id.Should().BeEquivalentTo(GenerateId(providerSummary.Id, buildProject.SpecificationId)); result.CalculationResults.Should().HaveCount(3); result.FundingLineResults.Should().HaveCount(1); CalculationResult fundingCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == fundingCalcReference.Id); fundingCalcResult.Calculation.Should().BeEquivalentTo(fundingCalcReference); fundingCalcResult.CalculationType.Should().BeEquivalentTo(CalculationType.Template); fundingCalcResult.Value.Should().Be(fundingCalcReturned.Value); fundingCalcResult.CalculationDataType.Should().Be(CalculationDataType.Decimal); CalculationResult numberCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == numbercalcReference.Id); numberCalcResult.Calculation.Should().BeEquivalentTo(numbercalcReference); numberCalcResult.CalculationType.Should().BeEquivalentTo(CalculationType.Template); numberCalcResult.Value.Should().Be(fundingCalcReturned2.Value); numberCalcResult.CalculationDataType.Should().Be(CalculationDataType.Decimal); CalculationResult booleanCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == booleancalcReference.Id); booleanCalcResult.Calculation.Should().BeEquivalentTo(booleancalcReference); booleanCalcResult.CalculationType.Should().BeEquivalentTo(CalculationType.Template); booleanCalcResult.Value.Should().Be(fundingCalcReturned3.Value); booleanCalcResult.CalculationDataType.Should().Be(CalculationDataType.Boolean); FundingLineResult fundingLineCalcResult = result.FundingLineResults.First(cr => cr.FundingLine.Id == fundingLineCalcReference.Id); fundingLineCalcResult.FundingLine.Should().BeEquivalentTo(fundingLineCalcReference); fundingLineCalcResult.Value.Should().Be(fundingLineResult.Value); fundingLineCalcResult.FundingLineFundingStreamId.Should().Be(fundingStreamId); }
public async Task <IActionResult> PreviewCalculationResult( string specificationId, string providerId, PreviewCalculationRequest previewCalculationRequest) { Guard.IsNullOrWhiteSpace(specificationId, nameof(specificationId)); Guard.IsNullOrWhiteSpace(providerId, nameof(providerId)); Guard.ArgumentNotNull(previewCalculationRequest, nameof(previewCalculationRequest)); Assembly assembly = Assembly.Load(previewCalculationRequest.AssemblyContent); IAllocationModel allocationModel = _calculationEngine.GenerateAllocationModel(assembly); SpecificationSummary specificationSummary = await GetSpecificationSummary(specificationId); ApiResponse <ProviderVersionSearchResult> providerVersionSearchResultApiResponse = await _providersApiClientPolicy.ExecuteAsync(() => _providersApiClient.GetProviderByIdFromProviderVersion( specificationSummary.ProviderVersionId, providerId)); ProviderVersionSearchResult providerVersionSearchResult = providerVersionSearchResultApiResponse.Content; if (providerVersionSearchResult == null) { return(new NotFoundResult()); } ProviderSummary providerSummary = _mapper.Map <ProviderSummary>(providerVersionSearchResult); List <CalculationSummaryModel> calculationSummaries = new List <CalculationSummaryModel>(); IEnumerable <CalculationSummaryModel> specCalculationSummaries = await GetCalculationSummaries(specificationId); calculationSummaries.AddRange(specCalculationSummaries); calculationSummaries.Add(previewCalculationRequest.PreviewCalculationSummaryModel); Dictionary <string, Dictionary <string, ProviderSourceDataset> > providerSourceDatasets = await _providerSourceDatasetsRepository.GetProviderSourceDatasetsByProviderIdsAndRelationshipIds( specificationId, new[] { providerId }, specificationSummary.DataDefinitionRelationshipIds); Dictionary <string, ProviderSourceDataset> providerSourceDataset = providerSourceDatasets[providerId]; BuildAggregationRequest buildAggregationRequest = new BuildAggregationRequest { SpecificationId = specificationId, GenerateCalculationAggregationsOnly = true, BatchCount = 100 }; IEnumerable <CalculationAggregation> calculationAggregations = await _calculationAggregationService.BuildAggregations(buildAggregationRequest); ProviderResult providerResult = _calculationEngine.CalculateProviderResults( allocationModel, specificationId, calculationSummaries, providerSummary, providerSourceDataset, calculationAggregations ); return(new OkObjectResult(providerResult)); }
private async Task <CalculationResultsModel> CalculateResults(IEnumerable <ProviderSummary> summaries, IEnumerable <CalculationSummaryModel> calculations, IEnumerable <CalculationAggregation> aggregations, BuildProject buildProject, GenerateAllocationMessageProperties messageProperties, int providerBatchSize, int index, Stopwatch providerSourceDatasetsStopwatch, Stopwatch calculationStopwatch) { ConcurrentBag <ProviderResult> providerResults = new ConcurrentBag <ProviderResult>(); IEnumerable <ProviderSummary> partitionedSummaries = summaries.Skip(index).Take(providerBatchSize); IList <string> providerIdList = partitionedSummaries.Select(m => m.Id).ToList(); providerSourceDatasetsStopwatch.Start(); _logger.Information($"Fetching provider sources for specification id {messageProperties.SpecificationId}"); List <ProviderSourceDataset> providerSourceDatasets = new List <ProviderSourceDataset>(await _providerSourceDatasetsRepositoryPolicy.ExecuteAsync(() => _providerSourceDatasetsRepository.GetProviderSourceDatasetsByProviderIdsAndSpecificationId(providerIdList, messageProperties.SpecificationId))); providerSourceDatasetsStopwatch.Stop(); if (providerSourceDatasets == null) { _logger.Information($"No provider sources found for specification id {messageProperties.SpecificationId}"); providerSourceDatasets = new List <ProviderSourceDataset>(); } _logger.Information($"fetched provider sources found for specification id {messageProperties.SpecificationId}"); calculationStopwatch.Start(); _logger.Information($"calculating results for specification id {messageProperties.SpecificationId}"); Assembly assembly = Assembly.Load(buildProject.Build.Assembly); Parallel.ForEach(partitionedSummaries, new ParallelOptions { MaxDegreeOfParallelism = _engineSettings.CalculateProviderResultsDegreeOfParallelism }, provider => { IAllocationModel allocationModel = _calculationEngine.GenerateAllocationModel(assembly); IEnumerable <ProviderSourceDataset> providerDatasets = providerSourceDatasets.Where(m => m.ProviderId == provider.Id); ProviderResult result = _calculationEngine.CalculateProviderResults(allocationModel, buildProject, calculations, provider, providerDatasets, aggregations); if (result != null) { providerResults.Add(result); } else { throw new InvalidOperationException("Null result from Calc Engine CalculateProviderResults"); } }); _logger.Information($"calculating results complete for specification id {messageProperties.SpecificationId}"); calculationStopwatch.Stop(); return(new CalculationResultsModel { ProviderResults = providerResults, PartitionedSummaries = partitionedSummaries }); }