public void GenerateCalculations_GiveEnumCalculationType()
        {
            Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("1619")))
                                               .WithName("MethodologyType")
                                               .WithValueType(CalculationValueType.String)
                                               .WithDataType(CalculationDataType.Enum)
                                               .WithAllowedEnumTypeValues(new[] { "Type1", "Type2", "Type3" })
                                               .WithSourceCodeName("One")
                                               .WithCalculationNamespaceType(CalculationNamespace.Template)
                                               .WithSourceCode("return Nothing"));

            FundingLine _1619fl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1)
                                                                                                           .WithCalculationNamespaceType(CalculationNamespace.Template)) })
                                                 .WithId(2)
                                                 .WithName("Two")
                                                 .WithSourceCodeName("Two")
                                                 .WithNamespace("1619"));

            IDictionary <string, Funding> fundingLines = new Dictionary <string, Funding> {
                { "1619", NewFunding(_ => _.WithFundingLines(new[] { _1619fl })) }
            };

            IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619 }, fundingLines).ToList();

            results.Should().HaveCount(1);

            results.First().SourceCode.Should().Contain("Public Enum MethodologyTypeOptions");
            results.First().SourceCode.Should().Contain("Public One As Func(Of MethodologyTypeOptions?) = Nothing");
            results.First().SourceCode.Should().Contain("Return If(String.IsNullOrWhiteSpace(existingCalculationResultString), Nothing, CType([Enum].Parse(GetType(MethodologyTypeOptions), existingCalculationResultString), MethodologyTypeOptions?))");
            results.First().SourceCode.Should().Contain($"calculationContext.DictionaryStringValues.Add(\"{_1619.Id}\", executedUserCodeCalculationResult?.ToString())");
        }
        public void GenerateCalculations_GivenSpaceInEnumReturnsValidEnumCalculationType()
        {
            Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("1619")))
                                               .WithName("MethodologyType")
                                               .WithValueType(CalculationValueType.String)
                                               .WithDataType(CalculationDataType.Enum)
                                               .WithAllowedEnumTypeValues(new[] { "Type1 ", "Type2", "Type3" })
                                               .WithSourceCodeName("One")
                                               .WithCalculationNamespaceType(CalculationNamespace.Template)
                                               .WithSourceCode("return Nothing"));

            FundingLine _1619fl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1)
                                                                                                           .WithCalculationNamespaceType(CalculationNamespace.Template)) })
                                                 .WithId(2)
                                                 .WithName("Two")
                                                 .WithSourceCodeName("Two")
                                                 .WithNamespace("1619"));

            IDictionary <string, Funding> fundingLines = new Dictionary <string, Funding> {
                { "1619", NewFunding(_ => _.WithFundingLines(new[] { _1619fl })) }
            };

            IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619 }, fundingLines).ToList();

            results.Should().HaveCount(1);

            results.First().SourceCode.Should().Contain("Enum MethodologyTypeOptions");
            results.First().SourceCode.Should().Contain("Public One As Func(Of MethodologyTypeOptions?) = Nothing");
        }
        public void GenerateCalculations_GivenEnumCalculation_ThenEnumCreated()
        {
            Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("1619")))
                                               .WithSourceCodeName("One")
                                               .WithCalculationNamespaceType(CalculationNamespace.Template)
                                               .WithSourceCode("return 456")
                                               .WithName("EnumName")
                                               .WithDataType(CalculationDataType.Enum)
                                               .WithAllowedEnumTypeValues(new[] { "Option1", "Option2" }));

            FundingLine _1619fl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1)
                                                                                                           .WithCalculationNamespaceType(CalculationNamespace.Template)) })
                                                 .WithId(1)
                                                 .WithName("One")
                                                 .WithSourceCodeName("One")
                                                 .WithNamespace("1619"));

            IDictionary <string, Funding> fundingLines = new Dictionary <string, Funding> {
                { "1619", NewFunding(_ => _.WithFundingLines(new[] { _1619fl })) },
            };

            IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619 }, fundingLines);

            results.Should().HaveCount(1);
            results.First()
            .SourceCode
            .Should()
            .Contain(
                @$ "Enum EnumNameOptions
        <Description(Description:=" "Option1" ")>
        Option1
        <Description(Description:=" "Option2" ")>
        Option2
    End Enum");
        }
        public void GenerateCalculations_GivenCalculationsInDifferentNamespaces_ThenInnerClassPerNamespaceCreated()
        {
            Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("1619")))
                                               .WithSourceCodeName("One")
                                               .WithCalculationNamespaceType(CalculationNamespace.Template)
                                               .WithSourceCode("return 456"));
            Calculation psg = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("PSG")))
                                             .WithCalculationNamespaceType(CalculationNamespace.Template)
                                             .WithSourceCodeName("One")
                                             .WithSourceCode("return Calculations.One() + 100"));
            Calculation additionalOne = NewCalculation(_ => _.WithFundingStream(NewReference())
                                                       .WithCalculationNamespaceType(CalculationNamespace.Additional)
                                                       .WithSourceCodeName("One")
                                                       .WithSourceCode("return DSG.One() + Calculations.Two() + 34"));
            Calculation additionalTwo = NewCalculation(_ => _.WithFundingStream(NewReference())
                                                       .WithCalculationNamespaceType(CalculationNamespace.Additional)
                                                       .WithSourceCodeName("Two")
                                                       .WithSourceCode("return 789"));

            Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>();

            IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619, psg, additionalOne, additionalTwo }, fundingLines);

            results.Should().HaveCount(1);
            results.First()
            .SourceCode
            .Should()
            .ContainAll("Public Class PSGCalculations",
                        "Public Class _1619Calculations",
                        "Public Class AdditionalCalculations");
        }
コード例 #5
0
        private void PopulateCachedCalculationAggregationsBatch(IEnumerable <ProviderResult> providerResults, Dictionary <string, List <decimal> > cachedCalculationAggregationsBatch, GenerateAllocationMessageProperties messageProperties)
        {
            if (cachedCalculationAggregationsBatch == null)
            {
                _logger.Error($"Cached calculation aggregations not found for key: {messageProperties.CalculationsAggregationsBatchCacheKey}");

                throw new Exception($"Cached calculation aggregations not found for key: {messageProperties.CalculationsAggregationsBatchCacheKey}");
            }

            IEnumerable <string> calculationsToAggregate = messageProperties.CalculationsToAggregate;

            foreach (ProviderResult providerResult in providerResults)
            {
                IEnumerable <CalculationResult> calculationResultsForAggregation = providerResult.CalculationResults.Where(m =>
                                                                                                                           calculationsToAggregate.Contains(VisualBasicTypeGenerator.GenerateIdentifier(m.Calculation.Name), StringComparer.InvariantCultureIgnoreCase));

                foreach (CalculationResult calculationResult in calculationResultsForAggregation)
                {
                    string calculationReferenceName = CalculationTypeGenerator.GenerateIdentifier(calculationResult.Calculation.Name.Trim());

                    string calcNameFromCalcsToAggregate = messageProperties.CalculationsToAggregate.FirstOrDefault(m => string.Equals(m, calculationReferenceName, StringComparison.InvariantCultureIgnoreCase));

                    if (!string.IsNullOrWhiteSpace(calcNameFromCalcsToAggregate) && cachedCalculationAggregationsBatch.ContainsKey(calculationReferenceName))
                    {
                        cachedCalculationAggregationsBatch[calcNameFromCalcsToAggregate].Add(calculationResult.Value.HasValue ? calculationResult.Value.Value : 0);
                    }
                }
            }
        }
コード例 #6
0
        public void GenerateIdentifier_IdentifiersSubstituted(string input, string expected)
        {
            // Act
            string result = CalculationTypeGenerator.GenerateIdentifier(input);

            // Assert
            result
            .Should()
            .Be(expected);
        }
コード例 #7
0
        public void QuoteAggregateFunctionCalls_QuotesAsExpected(string input, string expected)
        {
            //Act
            string result = CalculationTypeGenerator.QuoteAggregateFunctionCalls(input);

            //Assert
            result
            .Should()
            .Be(expected);
        }
        public void GenerateCalculations_GivenNoCalculations_ThenSingleInnerClassForAdditionalCreated()
        {
            Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>();

            IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(Enumerable.Empty <Calculation>(), fundingLines);

            results.Should().HaveCount(1);
            results.First()
            .SourceCode
            .Should()
            .ContainAll("Public Class AdditionalCalculations");
        }
        public void GenerateCalcs_InvalidSourceCodeNormaliseWhitespaceFails_ReturnsError()
        {
            CompilerOptions          compilerOptions          = new CompilerOptions();
            CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions, _fundingLineRoundingSettings.Object);

            string badCode = @"Dim Filter as Decimal
Dim APTPhase as String
Dim CensusPhase as String
Dim Result as Decimal

Filter = FILTERSBSAcademiesFilter()
APTPhase = Datasets.APTInputsAndAdjustments.Phase()
CensusPhase = Datasets.CensusPupilCharacteristics.Phase()

If Filter = 0 then

Return Exclude()

Else

If string.isnullorempty(APTPhase) then Result = 0

Else If CensusPhase = ""PRIMARY"" then Result = 1

Else If CensusPhase = ""MIDDLE-DEEMED PRIMARY"" then Result = 2

Else If CensusPhase = ""SECONDARY"" then Result = 3

Else If CensusPhase = ""MIDDLE-DEEMED SECONDARY"" then Result = 4

End If
End If
End If

Return Result + 0";

            IEnumerable <Calculation> calculations = new[] { new Calculation {
                                                                 Current = new CalculationVersion {
                                                                     SourceCode = badCode, SourceCodeName = "Broken"
                                                                 }
                                                             } };

            Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>();

            Action generate = () => calculationTypeGenerator.GenerateCalcs(calculations, fundingLines).ToList();

            generate
            .Should()
            .Throw <Exception>()
            .And.Message
            .Should()
            .StartWith("Error compiling source code. Please check your code's structure is valid. ");
        }
コード例 #10
0
        public void GenerateCalcs_GivenCalculationsAndCompilerOptionsUseLegacyCodeIsSet_TheEnsuresCorrectInheritanceStatement(bool useLegacyCode, string expectedInheritsStatement)
        {
            // Arrange
            CompilerOptions compilerOptions = new CompilerOptions
            {
                UseLegacyCode = useLegacyCode,
            };

            CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions);

            // Act
            IEnumerable <SourceFile> results = calculationTypeGenerator.GenerateCalcs(new List <Calculation>());

            // Assert
            results.Should().HaveCount(1);

            results.First().SourceCode.Should().Contain(expectedInheritsStatement);
        }
コード例 #11
0
        public void GenerateCalcs_GivenCalculationsAndCompilerOptionsOff_ThenOptionsGenerated()
        {
            // Arrange
            List <Calculation> calculations = new List <Calculation>();

            CompilerOptions compilerOptions = new CompilerOptions
            {
                OptionStrictEnabled = false
            };

            CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions);

            // Act
            IEnumerable <SourceFile> results = calculationTypeGenerator.GenerateCalcs(calculations);

            // Assert
            results.Should().HaveCount(1);
            results.First().SourceCode.Should().StartWith("Option Strict Off");
        }
        public void GenerateCalculations_GivenNoAdditionalCalculations_ThenFundingLineMembersCreated()
        {
            Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("DSG")))
                                               .WithSourceCodeName("One")
                                               .WithCalculationNamespaceType(CalculationNamespace.Template)
                                               .WithSourceCode("return 456"));
            Calculation psg = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("PSG")))
                                             .WithCalculationNamespaceType(CalculationNamespace.Template)
                                             .WithSourceCodeName("Two")
                                             .WithSourceCode("return _1619.One() + 100"));

            FundingLine _1619fl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1)
                                                                                                           .WithCalculationNamespaceType(CalculationNamespace.Template)) })
                                                 .WithId(1)
                                                 .WithName("One")
                                                 .WithSourceCodeName("One")
                                                 .WithNamespace("1619"));

            FundingLine psgfl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1)
                                                                                                         .WithCalculationNamespaceType(CalculationNamespace.Template)) })
                                               .WithId(2)
                                               .WithName("Two")
                                               .WithSourceCodeName("Two")
                                               .WithNamespace("PSG"));

            IDictionary <string, Funding> fundingLines = new Dictionary <string, Funding> {
                { "1619", NewFunding(_ => _.WithFundingLines(new[] { _1619fl })) },
                { "PSG", NewFunding(_ => _.WithFundingLines(new[] { psgfl })) }
            };

            IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619, psg }, fundingLines);

            results.Should().HaveCount(1);
            results.First()
            .SourceCode
            .Should()
            .ContainAll(
                "<FundingLine(FundingStream:=\"1619\", Id:=\"1\", Name:=\"One\")>",
                "Public One As Func(Of decimal?) = Nothing",
                "<FundingLine(FundingStream:=\"PSG\", Id:=\"2\", Name:=\"Two\")>",
                "Public Two As Func(Of decimal?) = Nothing",
                "_1619.FundingLines.One()");
        }
        public void GenerateCalculations_GivenNoAdditionalCalculations_ThenSingleInnerClassForAdditionalStillCreated()
        {
            Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("1619")))
                                               .WithSourceCodeName("One")
                                               .WithCalculationNamespaceType(CalculationNamespace.Template)
                                               .WithSourceCode("return 456"));
            Calculation psg = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("PSG")))
                                             .WithCalculationNamespaceType(CalculationNamespace.Template)
                                             .WithSourceCodeName("One")
                                             .WithSourceCode("return DSG.One() + 100"));

            FundingLine _1619fl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1)
                                                                                                           .WithCalculationNamespaceType(CalculationNamespace.Template)) })
                                                 .WithId(1)
                                                 .WithName("One")
                                                 .WithSourceCodeName("One")
                                                 .WithNamespace("1619"));

            FundingLine psgfl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1)
                                                                                                         .WithCalculationNamespaceType(CalculationNamespace.Template)) })
                                               .WithId(2)
                                               .WithName("One")
                                               .WithSourceCodeName("One")
                                               .WithNamespace("PSG"));

            IDictionary <string, Funding> fundingLines = new Dictionary <string, Funding> {
                { "1619", NewFunding(_ => _.WithFundingLines(new[] { _1619fl })) },
                { "PSG", NewFunding(_ => _.WithFundingLines(new[] { psgfl })) }
            };

            IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619, psg }, fundingLines);

            results.Should().HaveCount(1);
            results.First()
            .SourceCode
            .Should()
            .ContainAll("Public Class PSGCalculations",
                        "Public Class _1619Calculations",
                        "Public Class AdditionalCalculations",
                        "Public Class PSGFundingLines",
                        "Public Class _1619FundingLines");
        }
        public void GenerateCalcs_GivenCalculationsAndCompilerOptionsStrictOn_ThenOptionStrictGenerated()
        {
            // Arrange
            List <Calculation>           calculations = new List <Calculation>();
            Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>();

            CompilerOptions compilerOptions = new CompilerOptions
            {
                OptionStrictEnabled = true
            };

            CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions, _fundingLineRoundingSettings.Object);

            // Act
            IEnumerable <SourceFile> results = calculationTypeGenerator.GenerateCalcs(calculations, fundingLines);

            // Assert
            results.Should().HaveCount(1);
            results.First().SourceCode.Should().StartWith("Option Strict On");
        }
コード例 #15
0
        public void GenerateCalcs_MissingSourceCodeName_ReturnsError()
        {
            string id = "42";

            CompilerOptions          compilerOptions          = new CompilerOptions();
            CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions);

            IEnumerable <Calculation> calculations = new[] { new Calculation {
                                                                 Current = new CalculationVersion {
                                                                     SourceCode = "Return 1"
                                                                 }, Id = id
                                                             } };

            Action generate = () => calculationTypeGenerator.GenerateCalcs(calculations).ToList();

            generate
            .Should()
            .Throw <Exception>()
            .And.Message
            .Should()
            .Be($"Calculation source code name is not populated for calc {id}");
        }
        public void GenerateCalcs_ThenEntryFunctionGenerated()
        {
            // Arrange
            Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>();

            CompilerOptions compilerOptions = new CompilerOptions
            {
                OptionStrictEnabled = false
            };

            CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions, _fundingLineRoundingSettings.Object);

            // Act
            IEnumerable <SourceFile> results = calculationTypeGenerator.GenerateCalcs(new List <Calculation>(), fundingLines);

            // Assert
            results.Should().HaveCount(1);
            // Assert
            results.Should().HaveCount(1);
            results.First()
            .SourceCode
            .Should().
            Contain("Public MainCalc As Func(Of Boolean, (CalculationResults As Dictionary(Of String, String()), FundingLineResults As Dictionary(Of String, String()))) = Function(allCalculations)");
        }
        public void GenerateCalcs_MissingSourceCodeName_ReturnsError()
        {
            string id = "42";

            CompilerOptions          compilerOptions          = new CompilerOptions();
            CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions, _fundingLineRoundingSettings.Object);

            IEnumerable <Calculation> calculations = new[] { new Calculation {
                                                                 Current = new CalculationVersion {
                                                                     SourceCode = "Return 1", Namespace = CalculationNamespace.Additional
                                                                 }, Id = id
                                                             } };

            Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>();

            Action generate = () => calculationTypeGenerator.GenerateCalcs(calculations, fundingLines).ToList();

            generate
            .Should()
            .Throw <Exception>()
            .And.Message
            .Should()
            .Be($"Calculation source code name is not populated for calc {id}");
        }
コード例 #18
0
        private async Task <IEnumerable <Job> > CreateGenerateAllocationJobs(JobViewModel parentJob, IEnumerable <IDictionary <string, string> > jobProperties)
        {
            HashSet <string> calculationsToAggregate = new HashSet <string>();

            if (parentJob.JobDefinitionId == JobConstants.DefinitionNames.CreateInstructGenerateAggregationsAllocationJob)
            {
                string calculationAggregatesCacheKeyPrefix = $"{CacheKeys.CalculationAggregations}{parentJob.SpecificationId}";

                await _cacheProvider.RemoveByPatternAsync(calculationAggregatesCacheKeyPrefix);

                IEnumerable <Models.Calcs.Calculation> calculations = await _calculationsRepository.GetCalculationsBySpecificationId(parentJob.SpecificationId);

                foreach (Models.Calcs.Calculation calculation in calculations)
                {
                    IEnumerable <string> aggregateParameters = SourceCodeHelpers.GetCalculationAggregateFunctionParameters(calculation.Current.SourceCode);
                    if (aggregateParameters.IsNullOrEmpty())
                    {
                        continue;
                    }

                    foreach (string aggregateParameter in aggregateParameters)
                    {
                        Models.Calcs.Calculation referencedCalculation = calculations.FirstOrDefault(m => string.Equals(CalculationTypeGenerator.GenerateIdentifier(m.Name.Trim()), aggregateParameter.Trim(), StringComparison.InvariantCultureIgnoreCase));

                        if (referencedCalculation != null)
                        {
                            calculationsToAggregate.Add(aggregateParameter);
                        }
                    }
                }
            }

            IList <JobCreateModel> jobCreateModels = new List <JobCreateModel>();

            Trigger trigger = new Trigger
            {
                EntityId   = parentJob.Id,
                EntityType = nameof(Job),
                Message    = $"Triggered by parent job with id: '{parentJob.Id}"
            };

            int    batchNumber      = 1;
            int    batchCount       = jobProperties.Count();
            string calcsToAggregate = string.Join(",", calculationsToAggregate);

            foreach (IDictionary <string, string> properties in jobProperties)
            {
                properties.Add("batch-number", batchNumber.ToString());
                properties.Add("batch-count", batchCount.ToString());
                properties.Add("calculations-to-aggregate", calcsToAggregate);

                JobCreateModel jobCreateModel = new JobCreateModel
                {
                    InvokerUserDisplayName = parentJob.InvokerUserDisplayName,
                    InvokerUserId          = parentJob.InvokerUserId,
                    JobDefinitionId        = parentJob.JobDefinitionId == JobConstants.DefinitionNames.CreateInstructAllocationJob ? JobConstants.DefinitionNames.CreateAllocationJob : JobConstants.DefinitionNames.GenerateCalculationAggregationsJob,
                    SpecificationId        = parentJob.SpecificationId,
                    Properties             = properties,
                    ParentJobId            = parentJob.Id,
                    Trigger       = trigger,
                    CorrelationId = parentJob.CorrelationId
                };

                batchNumber++;

                jobCreateModels.Add(jobCreateModel);
            }

            return(await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.CreateJobs(jobCreateModels)));
        }