Esempio n. 1
0
        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);
        }