From 924fad02e3a0db50c9ce2c5c38a0a55f875db171 Mon Sep 17 00:00:00 2001 From: likp Date: Tue, 15 Oct 2024 12:01:07 +0200 Subject: [PATCH 1/8] With pivot implementation --- PxWeb.UnitTests/Data/DefaultSelectionTest.cs | 30 +-- PxWeb.sln | 2 +- .../Api2/DataSelection/ISelectionHandler.cs | 2 +- .../Api2/DataSelection/SelectionHandler.cs | 85 ++++++--- PxWeb/Controllers/Api2/TableApiController.cs | 112 ++++++++--- PxWeb/Mappers/CodelistResponseMapper.cs | 4 +- PxWeb/Mappers/DatasetMapper.cs | 88 ++++++--- PxWeb/Mappers/ISelectionResponseMapper.cs | 2 +- PxWeb/Mappers/SelectionResponseMapper.cs | 8 +- PxWeb/Mappers/TableMetadataResponseMapper.cs | 7 +- PxWeb/Models/Api2/DatasetSubclass.cs | 174 ++++++++++++++++++ PxWeb/PxWeb.csproj | 2 +- 12 files changed, 412 insertions(+), 104 deletions(-) diff --git a/PxWeb.UnitTests/Data/DefaultSelectionTest.cs b/PxWeb.UnitTests/Data/DefaultSelectionTest.cs index 4e74c61a..786b0f23 100644 --- a/PxWeb.UnitTests/Data/DefaultSelectionTest.cs +++ b/PxWeb.UnitTests/Data/DefaultSelectionTest.cs @@ -13,7 +13,7 @@ public void ShouldReturnSingleSelectionFromFallbackWith1500ValuesSelected() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -31,7 +31,7 @@ public void ShouldReturnSingleSelectionFromFallbackWithOriginalNumberOfValuesSel Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -49,7 +49,7 @@ public void ShouldHave2SelectionsByFallback() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -66,7 +66,7 @@ public void MandantoryVariableShouldBeSelectedByFallback() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -98,7 +98,7 @@ public void OnlyMandantoryVariableShouldBeSelectedByFallback() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -130,7 +130,7 @@ public void OnlyMandantoryVariableShouldBeSelectedThirdVariableShouldOnlyHaveOne Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -162,7 +162,7 @@ public void CaseA1_ShouldReturnContentsInHeadAndTimeInStub() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -177,7 +177,7 @@ public void CaseA1_ShouldReturnContentsInHeadAndTimeInStub() Assert.IsNotNull(contens); Assert.AreEqual(contens.ValueCodes.Count, 3); Assert.IsNotNull(time); - Assert.AreEqual(time.ValueCodes.Count, 30); + Assert.AreEqual(time.ValueCodes.Count, 13); } [TestMethod] @@ -189,7 +189,7 @@ public void CaseA2_ShouldReturnContentsInHeadAndTimeInStubMax13TimeValues() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -216,7 +216,7 @@ public void CaseB1_ShouldReturn13TimePeriodsAndClassificationVariable() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -246,7 +246,7 @@ public void CaseB2_ShouldReturn1TimePeriodsAndContentsClassificationVariable() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -276,7 +276,7 @@ public void CaseC1_ShouldReturnFirstAndLastClassificationVariable() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -312,7 +312,7 @@ public void CaseC2_ShouldReturnFirstMandantoryAndLastClassificationVariable() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -348,7 +348,7 @@ public void CaseC2_ShouldReturnThe2MandantoryVariables() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); @@ -384,7 +384,7 @@ public void CaseC2_ShouldReturnTheFirstAndLastMandantoryVariables() Problem? problem; // Act - var selection = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); + var (selection, heading, stub) = selectionHandler.GetDefaultSelection(builderMock.Object, out problem); // Assert Assert.IsNull(problem); diff --git a/PxWeb.sln b/PxWeb.sln index 6d4e7c31..66b33935 100644 --- a/PxWeb.sln +++ b/PxWeb.sln @@ -15,7 +15,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Px.Search.Lucene", "Px.Sear EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PxWebApi.BigTests", "PxWebApi.BigTests\PxWebApi.BigTests.csproj", "{E52A7944-28C6-4004-9282-1AE8BC539F8F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PxWebApi_Mvc.Tests", "PxWebApi_Mvc.Tests\PxWebApi_Mvc.Tests.csproj", "{DE680238-98E9-4FCE-9D55-9B849F893238}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PxWebApi_Mvc.Tests", "PxWebApi_Mvc.Tests\PxWebApi_Mvc.Tests.csproj", "{DE680238-98E9-4FCE-9D55-9B849F893238}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/PxWeb/Code/Api2/DataSelection/ISelectionHandler.cs b/PxWeb/Code/Api2/DataSelection/ISelectionHandler.cs index 44aa9108..ef2ea658 100644 --- a/PxWeb/Code/Api2/DataSelection/ISelectionHandler.cs +++ b/PxWeb/Code/Api2/DataSelection/ISelectionHandler.cs @@ -8,6 +8,6 @@ public interface ISelectionHandler { public bool UseDefaultSelection(VariablesSelection? variablesSelection); public Selection[]? GetSelection(IPXModelBuilder builder, VariablesSelection variablesSelection, out Problem? problem); - public Selection[]? GetDefaultSelection(IPXModelBuilder builder, out Problem? problem); + public (Selection[]?, List, List) GetDefaultSelection(IPXModelBuilder builder, out Problem? problem); } } diff --git a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs index 42dc60bb..35be8e89 100644 --- a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs +++ b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs @@ -1341,7 +1341,7 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) return variablesSelection is null || !HasSelection(variablesSelection); } - public Selection[]? GetDefaultSelection(IPXModelBuilder builder, out Problem? problem) + public (Selection[]?, List, List) GetDefaultSelection(IPXModelBuilder builder, out Problem? problem) { var meta = builder.Model.Meta; // Default groupings and value sets are applied in the SQL parser by default @@ -1354,52 +1354,52 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) } } - var contents = meta.Variables.FirstOrDefault(v => v.IsContentVariable); var time = meta.Variables.FirstOrDefault(v => v.IsTime); List selections; + List heading; + List stub; if (contents is not null && time is not null) { //PX file using good practice or CNMM datasource - selections = GetDefaultSelectionByAlgorithm(meta, contents, time); + (selections, heading, stub) = GetDefaultSelectionByAlgorithm(meta, contents, time); } else { - selections = GetDefaultSelectionByAlgorithmFallback(meta); + (selections, heading, stub) = GetDefaultSelectionByAlgorithmFallback(meta); } - // Case A according to algorithm - //TODO: Pivot information missing - - //Verify that valid selections could be made for mandatory variables if (!VerifyMadeSelection(builder, selections.ToArray())) { problem = ProblemUtility.IllegalSelection(); - return null; + return (null, new List(), new List()); } if (!CheckNumberOfCells(selections.ToArray())) { problem = ProblemUtility.TooManyCellsSelected(); - return null; + return (null, new List(), new List()); } problem = null; - return selections.ToArray(); + return (selections.ToArray(), heading, stub); } - private List GetDefaultSelectionByAlgorithmFallback(PXMeta meta) + private (List, List, List) GetDefaultSelectionByAlgorithmFallback(PXMeta meta) { var selections = new List(); + List placmentHeading = new List(); + List placmentStub = new List(); - //Only one variable put it in the stub + //Only one variable put it in the placmentStub if (meta.Variables.Count == 1) { selections.AddStubVariable(meta.Variables[0], GetCodes); - return selections; + placmentStub.Add(meta.Variables[0].Code); + return (selections, placmentHeading, placmentStub); } var mandatoryClassificationVariables = meta.Variables.Where(v => v.Elimination == false).ToList(); @@ -1408,11 +1408,14 @@ private List GetDefaultSelectionByAlgorithmFallback(PXMeta meta) if (mandatoryClassificationVariables.Count == 1) //Only one mandantory classification variable { //Take the mandantory and the last none mandantory classification variable - // place the one with most values in the stub + // place the one with most values in the placmentStub var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], noneMandatoryClassificationVariables.Last()); selections.AddStubVariable(stub, GetCodes); selections.AddHeadingVariable(heading, GetCodes); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); + //Eliminate all none mandatory classification variables for (int i = 0; i < noneMandatoryClassificationVariables.Count - 1; i++) { @@ -1422,7 +1425,7 @@ private List GetDefaultSelectionByAlgorithmFallback(PXMeta meta) else if (mandatoryClassificationVariables.Count > 1) // Two or more mandatory classification variable { //Take the first and last mandantory classification variable - //and place the one with most values in the stub + //and place the one with most values in the placmentStub var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], mandatoryClassificationVariables.Last()); selections.AddStubVariable(stub, GetCodes); selections.AddHeadingVariable(heading, GetCodes); @@ -1431,6 +1434,7 @@ private List GetDefaultSelectionByAlgorithmFallback(PXMeta meta) for (int i = 1; i < mandatoryClassificationVariables.Count - 1; i++) { selections.AddVariableToHeading(mandatoryClassificationVariables[i], GetCodes); + placmentHeading.Add(mandatoryClassificationVariables[i].Code); } //Eliminate all none mandatory classification variables @@ -1438,12 +1442,17 @@ private List GetDefaultSelectionByAlgorithmFallback(PXMeta meta) { selections.EliminateVariable(noneMandatoryClassificationVariables[i]); } + + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); } else //No mandantory variables and at leat two of them { //Take the first and last none mandantory classification variable - //and place the one with most values in the stub + //and place the one with most values in the placmentStub var (stub, heading) = StubOrHeading(noneMandatoryClassificationVariables[0], noneMandatoryClassificationVariables.Last()); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); //Eliminate all none mandatory classification variables for (int i = 1; i < noneMandatoryClassificationVariables.Count - 1; i++) @@ -1452,25 +1461,31 @@ private List GetDefaultSelectionByAlgorithmFallback(PXMeta meta) } } - return selections; + return (selections, placmentHeading, placmentStub); } - private List GetDefaultSelectionByAlgorithm(PXMeta meta, Variable contents, Variable time) + private (List, List, List) GetDefaultSelectionByAlgorithm(PXMeta meta, Variable contents, Variable time) { var selections = new List(); + List placmentHeading = new List(); + List placmentStub = new List(); - if (meta.Variables.Count == 2) + if (meta.Variables.Count == 2) // Case A according to algorithm { // PX table using good practice och CNMM datasource if (contents.Values.Count < 6) { selections.AddHeadingVariable(contents, GetCodes); - selections.AddStubVariable(time, GetTimeCodes); + selections.AddStubVariable(time, GetTimeCodes, 13); + placmentHeading.Add(contents.Code); + placmentStub.Add(time.Code); } else { selections.AddStubVariable(contents, GetCodes); selections.AddHeadingVariable(time, GetTimeCodes, 13); + placmentHeading.Add(time.Code); + placmentStub.Add(contents.Code); } } else if (meta.Variables.Count == 3) // Case B @@ -1480,11 +1495,14 @@ private List GetDefaultSelectionByAlgorithm(PXMeta meta, Variable con // select the contents and 13 latest time values selections.AddVariableToHeading(contents, GetCodes); selections.AddHeadingVariable(time, GetTimeCodes, 13); + placmentHeading.Add(contents.Code); + placmentHeading.Add(time.Code); var variable = meta.Variables.FirstOrDefault(v => v.Code != contents.Code && v.Code != time.Code); if (variable != null) { selections.AddStubVariable(variable, GetCodes); + placmentStub.Add(variable.Code); } } else @@ -1497,12 +1515,15 @@ private List GetDefaultSelectionByAlgorithm(PXMeta meta, Variable con //Add the latest time value selections.AddVariableToHeading(time, GetTimeCodes); + placmentHeading.Add(time.Code); - //Check if contents of classification should be in stub or heading + //Check if contents of classification should be in placmentStub or placmentHeading var (stub, heading) = StubOrHeading(contents, variable); selections.AddStubVariable(stub, GetCodes); selections.AddHeadingVariable(heading, GetCodes); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); } } else // Case C @@ -1510,6 +1531,7 @@ private List GetDefaultSelectionByAlgorithm(PXMeta meta, Variable con //First content and lastNoneMandantoryClassificationVariable time period selections.AddVariableToHeading(contents, GetCodes); selections.AddVariableToHeading(time, GetTimeCodes); + placmentHeading.Add(contents.Code); var classificationVariables = meta.Variables.Where(v => v.Code != contents.Code && v.Code != time.Code).ToList(); @@ -1521,10 +1543,13 @@ private List GetDefaultSelectionByAlgorithm(PXMeta meta, Variable con for (int i = 1; i < (mandatoryClassificationVariables.Count - 1); i++) { selections.AddVariableToHeading(mandatoryClassificationVariables[i], GetCodes); + placmentHeading.Add(mandatoryClassificationVariables[i].Code); } - //The variable with the most values should be in the heading + //The variable with the most values should be in the placmentHeading var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], mandatoryClassificationVariables[mandatoryClassificationVariables.Count - 1]); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); selections.AddStubVariable(stub, GetCodes); selections.AddHeadingVariable(heading, GetCodes); @@ -1542,6 +1567,10 @@ private List GetDefaultSelectionByAlgorithm(PXMeta meta, Variable con selections.AddStubVariable(stub, GetCodes); selections.AddHeadingVariable(heading, GetCodes); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); + + foreach (var variable in noneMandatoryClassificationVariables) { if (variable != lastNoneMandantoryClassificationVariable) @@ -1557,6 +1586,8 @@ private List GetDefaultSelectionByAlgorithm(PXMeta meta, Variable con var (stub, heading) = StubOrHeading(firstNoneMandantoryClassificationVariable, lastNoneMandantoryClassificationVariable); selections.AddStubVariable(stub, GetCodes); selections.AddHeadingVariable(heading, GetCodes); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); foreach (var variable in noneMandatoryClassificationVariables) @@ -1567,9 +1598,11 @@ private List GetDefaultSelectionByAlgorithm(PXMeta meta, Variable con } } } + //place time as last variable in heading + placmentHeading.Add(time.Code); } - return selections; + return (selections, placmentHeading, placmentStub); } private static (Variable, Variable) StubOrHeading(Variable one, Variable two) @@ -1588,10 +1621,10 @@ private static (Variable, Variable) StubOrHeading(Variable one, Variable two) public static class SelectionsExtensions { - public static void AddStubVariable(this List selections, Variable variable, Func valuesFunction) + public static void AddStubVariable(this List selections, Variable variable, Func valuesFunction, int numberOfValues = 1500) { var selection = new Selection(variable.Code); - selection.ValueCodes.AddRange(valuesFunction(variable, 1500)); + selection.ValueCodes.AddRange(valuesFunction(variable, numberOfValues)); selections.Add(selection); } diff --git a/PxWeb/Controllers/Api2/TableApiController.cs b/PxWeb/Controllers/Api2/TableApiController.cs index d33f9f06..e6c1ec22 100644 --- a/PxWeb/Controllers/Api2/TableApiController.cs +++ b/PxWeb/Controllers/Api2/TableApiController.cs @@ -24,7 +24,6 @@ using PxWeb.Api2.Server.Models; using PxWeb.Code.Api2.DataSelection; -using PxWeb.Code.Api2.ModelBinder; using PxWeb.Code.Api2.Serialization; using PxWeb.Helper.Api2; using PxWeb.Mappers; @@ -157,9 +156,9 @@ public override IActionResult ListAllTables([FromQuery(Name = "lang")] string? l } - public override IActionResult GetTableData([FromRoute(Name = "id"), Required] string id, [FromQuery(Name = "lang")] string? lang, [FromQuery(Name = "valuecodes"), ModelBinder(typeof(QueryStringToDictionaryOfStrings))] Dictionary>? valuecodes, [FromQuery(Name = "codelist")] Dictionary? codelist, [FromQuery(Name = "outputvalues")] Dictionary? outputvalues, [FromQuery(Name = "outputFormat")] string? outputFormat) + public override IActionResult GetTableData([FromRoute(Name = "id"), Required] string id, [FromQuery(Name = "lang")] string? lang, [FromQuery(Name = "valuecodes")] Dictionary>? valuecodes, [FromQuery(Name = "codelist")] Dictionary? codelist, [FromQuery(Name = "outputvalues")] Dictionary? outputvalues, [FromQuery(Name = "outputFormat")] string? outputFormat, [FromQuery(Name = "heading")] List? heading, [FromQuery(Name = "stub")] List? stub) { - VariablesSelection variablesSelection = MapDataParameters(valuecodes, codelist, outputvalues); + VariablesSelection variablesSelection = MapDataParameters(valuecodes, codelist, outputvalues, heading, stub); return GetData(id, lang, variablesSelection, outputFormat); } @@ -184,54 +183,60 @@ private IActionResult GetData(string id, string? lang, VariablesSelection? varia builder.BuildForSelection(); Selection[]? selection = null; - bool IsDefaultSelection = false; + //bool IsDefaultSelection = false; + VariablePlacementType? placment = null; + if (_selectionHandler.UseDefaultSelection(variablesSelection)) { - selection = _selectionHandler.GetDefaultSelection(builder, out problem); - IsDefaultSelection = true; + List heading, stub; + (selection, heading, stub) = _selectionHandler.GetDefaultSelection(builder, out problem); + placment = new VariablePlacementType() { Heading = heading, Stub = stub }; + + //IsDefaultSelection = true; } else { if (variablesSelection is not null) { selection = _selectionHandler.GetSelection(builder, variablesSelection, out problem); + + if (problem is not null) + { + //Check if we should pivot the table + placment = GetPlacment(variablesSelection, builder, out problem); + } } } - - if (problem is not null) { return BadRequest(problem); } - builder.BuildForPresentation(selection); var model = builder.Model; - if (IsDefaultSelection) + if (placment is not null) { - //TODO: Check if selection is default selection if so pivot the table - var variables = model.Meta.Variables.Select(v => new { Code = v.Name, NumberOfValues = v.Values.Count }).ToList(); - variables.Sort((a, b) => b.NumberOfValues.CompareTo(a.NumberOfValues)); - var descriptions = new List(); - if (variables.Count > 0) + + descriptions.AddRange(placment.Heading.Select(h => new PivotDescription() { - descriptions.Add(new PivotDescription() { VariableName = variables[0].Code, VariablePlacement = PlacementType.Stub }); + VariableName = model.Meta.Variables.First(v => v.Code == h).Name, + VariablePlacement = PlacementType.Heading + })); - foreach (var variable in variables.Skip(1).Reverse()) - { - descriptions.Add(new PivotDescription() { VariableName = variable.Code, VariablePlacement = PlacementType.Heading }); - } + descriptions.AddRange(placment.Stub.Select(h => new PivotDescription() + { + VariableName = model.Meta.Variables.First(v => v.Code == h).Name, + VariablePlacement = PlacementType.Stub + })); - var pivot = new PCAxis.Paxiom.Operations.Pivot(); - model = pivot.Execute(model, descriptions.ToArray()); - } + var pivot = new PCAxis.Paxiom.Operations.Pivot(); + model = pivot.Execute(model, descriptions.ToArray()); } - if (outputFormat == null) { outputFormat = _configOptions.DefaultOutputFormat; @@ -247,6 +252,52 @@ private IActionResult GetData(string id, string? lang, VariablesSelection? varia return Ok(); } + + private VariablePlacementType? GetPlacment(VariablesSelection variablesSelection, IPXModelBuilder builder, out Problem? problem) + { + VariablePlacementType? placment = null; + var p = variablesSelection.Palcement; + //Check if user have specified stub or heading + if (p is not null && (p.Heading.Count > 0 || p.Stub.Count > 0)) + { + if ((p.Heading.Count + p.Stub.Count) != builder.Model.Meta.Variables.Count) + { + //Check if only one variable is selected + if (p.Heading.Count == 0 || p.Stub.Count == 0) + { + List usedVariables = new List(); + usedVariables.AddRange(p.Heading); + usedVariables.AddRange(p.Stub); + + var unusedVariables = builder.Model.Meta.Variables.Select(v => v.Code).Except(usedVariables).ToList(); + + if (p.Heading.Count == 0) + { + placment = new VariablePlacementType() { Heading = unusedVariables, Stub = p.Stub }; + } + else + { + placment = new VariablePlacementType() { Heading = p.Heading, Stub = unusedVariables }; + } + } + else + { + //Not all variables are specified in placment + problem = ProblemUtility.IllegalSelection(); + return null; + } + } + else + { + placment = p; + } + } + + problem = null; + return placment; + } + + /// /// Map querystring parameters to VariablesSelection object /// @@ -254,7 +305,7 @@ private IActionResult GetData(string id, string? lang, VariablesSelection? varia /// /// /// - private VariablesSelection MapDataParameters(Dictionary>? valuecodes, Dictionary? codelist, Dictionary? outputvalues) + private VariablesSelection MapDataParameters(Dictionary>? valuecodes, Dictionary? codelist, Dictionary? outputvalues, List? heading, List? stub) { VariablesSelection selections = new VariablesSelection(); if (valuecodes != null) @@ -277,6 +328,10 @@ private VariablesSelection MapDataParameters(Dictionary>? v } } + selections.Palcement = new VariablePlacementType(); + selections.Palcement.Heading = heading ?? new List(); + selections.Palcement.Stub = stub ?? new List(); + return selections; } @@ -296,8 +351,9 @@ public override IActionResult GetDefaultSelection([FromRoute(Name = "id"), Requi builder.BuildForSelection(); - //No variable selection is provided, so we will return the default selection - var selection = _selectionHandler.GetDefaultSelection(builder, out problem); + //No variable selection is provided, so we will return the default selection + + var (selection, heading, stub) = _selectionHandler.GetDefaultSelection(builder, out problem); if (problem is not null || selection is null) { @@ -305,7 +361,7 @@ public override IActionResult GetDefaultSelection([FromRoute(Name = "id"), Requi } //Map selection to SelectionResponse - SelectionResponse selectionResponse = _selectionResponseMapper.Map(selection, builder.Model.Meta, id, lang); + SelectionResponse selectionResponse = _selectionResponseMapper.Map(selection, heading, stub, builder.Model.Meta, id, lang); return Ok(selectionResponse); } diff --git a/PxWeb/Mappers/CodelistResponseMapper.cs b/PxWeb/Mappers/CodelistResponseMapper.cs index 6d80c848..b31ac090 100644 --- a/PxWeb/Mappers/CodelistResponseMapper.cs +++ b/PxWeb/Mappers/CodelistResponseMapper.cs @@ -73,11 +73,11 @@ private ValueMap Map(CodelistValue val) map.Code = val.Code; map.Label = val.Label; - map._ValueMap = new System.Collections.Generic.List(); + map.VarValueMap = new System.Collections.Generic.List(); foreach (var v in val.ValueMap) { - map._ValueMap.Add(v.ToString()); + map.VarValueMap.Add(v.ToString()); } // TODO: Add later? diff --git a/PxWeb/Mappers/DatasetMapper.cs b/PxWeb/Mappers/DatasetMapper.cs index 281597b5..48f1f848 100644 --- a/PxWeb/Mappers/DatasetMapper.cs +++ b/PxWeb/Mappers/DatasetMapper.cs @@ -66,8 +66,11 @@ public Dataset Map(PXModel model, string id, string language) foreach (var variableValue in variable.Values) { - dimensionValue.Category.Label.Add(variableValue.Code, variableValue.Value); - dimensionValue.Category.Index.Add(variableValue.Code, indexCounter++); + if (dimensionValue.Category is not null) + { + dimensionValue.Category.Label.Add(variableValue.Code, variableValue.Value); + dimensionValue.Category.Index.Add(variableValue.Code, indexCounter++); + } CollectMetaIdsForValue(variableValue, ref metaIdsHelper); @@ -78,26 +81,30 @@ public Dataset Map(PXModel model, string id, string language) if (!variable.IsContentVariable) continue; var unitDecimals = (variableValue.HasPrecision()) ? variableValue.Precision : model.Meta.ShowDecimals; - dataset.AddUnitValue(dimensionValue.Category, out var unitValue); - - if (variableValue.ContentInfo != null) + if (dimensionValue.Category is not null) { - unitValue.Base = variableValue.ContentInfo.Units; - unitValue.Decimals = unitDecimals; + dataset.AddUnitValue(dimensionValue.Category, out var unitValue); - //refPeriod extension dimension - dataset.AddRefPeriod(dimensionValue, variableValue.Code, variableValue.ContentInfo.RefPeriod); + if (variableValue.ContentInfo != null) + { + unitValue.Base = variableValue.ContentInfo.Units; + unitValue.Decimals = unitDecimals; - // Contact - AddContact(dataset, variableValue.ContentInfo); - } - else - { - //TODO - // _logger.Warn("Category" + variableValue.Code + " lacks ContentInfo. Unit, refPeriod and contact not set"); + //refPeriod extension dimension + dataset.AddRefPeriod(dimensionValue, variableValue.Code, variableValue.ContentInfo.RefPeriod); + + // Contact + AddContact(dataset, variableValue.ContentInfo); + } + else + { + //TODO + // _logger.Warn("Category" + variableValue.Code + " lacks ContentInfo. Unit, refPeriod and contact not set"); + } + + dimensionValue.Category.Unit.Add(variableValue.Code, unitValue); } - dimensionValue.Category.Unit.Add(variableValue.Code, unitValue); } //elimination @@ -157,16 +164,21 @@ public Dataset Map(PXModel model, string id, string language) private void AddInfoForEliminatedContentVariable(PXModel model, DatasetSubclass dataset) { dataset.AddDimensionValue("ContentsCode", "EliminatedContents", out var dimensionValue); - dimensionValue.Category.Label.Add("EliminatedValue", model.Meta.Contents); - dimensionValue.Category.Index.Add("EliminatedValue", 0); - - dataset.AddUnitValue(dimensionValue.Category, out var unitValue); - unitValue.Base = model.Meta.ContentInfo.Units; - unitValue.Decimals = model.Meta.Decimals; + if (dimensionValue.Category is not null) + { + dimensionValue.Category.Label.Add("EliminatedValue", model.Meta.Contents); + dimensionValue.Category.Index.Add("EliminatedValue", 0); - dimensionValue.Category.Unit.Add("EliminatedValue", unitValue); + dataset.AddUnitValue(dimensionValue.Category, out var unitValue); + unitValue.Base = model.Meta.ContentInfo.Units; + unitValue.Decimals = model.Meta.Decimals; - dimensionValue.Extension.Elimination = true; + dimensionValue.Category.Unit.Add("EliminatedValue", unitValue); + } + if (dimensionValue.Extension is not null) + { + dimensionValue.Extension.Elimination = true; + } //refPeriod extension dimension dataset.AddRefPeriod(dimensionValue, "EliminatedValue", model.Meta.ContentInfo.RefPeriod); @@ -258,6 +270,10 @@ private void AddTableNotes(PXModel model, DatasetSubclass dataset) private void AddEliminationInfo(DatasetDimensionValue dimensionValue, Variable variable) { + if (dimensionValue.Extension is null) + { + dimensionValue.Extension = new ExtensionDimension(); + } dimensionValue.Extension.Elimination = variable.Elimination; if (!variable.Elimination || variable.EliminationValue == null) return; @@ -270,6 +286,10 @@ private void AddShow(DatasetDimensionValue dimensionValue, Variable variable) { if (Enum.TryParse(variable.PresentationText.ToString(), out PresentationFormType presentationForm)) { + if (dimensionValue.Extension is null) + { + dimensionValue.Extension = new ExtensionDimension(); + } dimensionValue.Extension.Show = presentationForm.ToString().ToLower(); } } @@ -325,7 +345,7 @@ private void AddContact(DatasetSubclass dataset, ContInfo contInfo) private void MapContact(DatasetSubclass dataset, PCAxis.Paxiom.Contact contact, ContInfo contInfo) { - if (dataset.Extension.Contact == null) + if (dataset.Extension is not null && dataset.Extension.Contact == null) { dataset.Extension.Contact = new List(); } @@ -354,7 +374,16 @@ private void MapContact(DatasetSubclass dataset, PCAxis.Paxiom.Contact contact, } // Only display unique contact once - if (!dataset.Extension.Contact.Exists(x => x.Mail.Equals(jsonContact.Mail) && x.Name.Equals(jsonContact.Name) && x.Phone.Equals(jsonContact.Phone))) + if (dataset.Extension is null) + { + dataset.Extension = new ExtensionRoot(); + } + if (!dataset.Extension.Contact.Exists(x => x.Mail is not null && + x.Name is not null && + x.Phone is not null && + x.Mail.Equals(jsonContact.Mail) && + x.Name.Equals(jsonContact.Name) && + x.Phone.Equals(jsonContact.Phone))) { dataset.Extension.Contact.Add(jsonContact); } @@ -364,6 +393,11 @@ private void MapContact(DatasetSubclass dataset, string contactString) { if (contactString != null) { + if (dataset.Extension is null) + { + dataset.Extension = new ExtensionRoot(); + } + if (dataset.Extension.Contact == null) { dataset.Extension.Contact = new List(); diff --git a/PxWeb/Mappers/ISelectionResponseMapper.cs b/PxWeb/Mappers/ISelectionResponseMapper.cs index 5ab36a17..27a1d686 100644 --- a/PxWeb/Mappers/ISelectionResponseMapper.cs +++ b/PxWeb/Mappers/ISelectionResponseMapper.cs @@ -6,6 +6,6 @@ namespace PxWeb.Mappers { public interface ISelectionResponseMapper { - SelectionResponse Map(Selection[] selections, PXMeta meta, string tableId, string lang); + SelectionResponse Map(Selection[] selections, List heading, List stub, PXMeta meta, string tableId, string lang); } } diff --git a/PxWeb/Mappers/SelectionResponseMapper.cs b/PxWeb/Mappers/SelectionResponseMapper.cs index f374cf84..907df2ec 100644 --- a/PxWeb/Mappers/SelectionResponseMapper.cs +++ b/PxWeb/Mappers/SelectionResponseMapper.cs @@ -16,7 +16,7 @@ public SelectionResponseMapper(ILinkCreator linkCreator) _linkCreator = linkCreator; } - public SelectionResponse Map(Selection[] selections, PXMeta meta, string tableId, string lang) + public SelectionResponse Map(Selection[] selections, List heading, List stub, PXMeta meta, string tableId, string lang) { var response = new SelectionResponse(); response.Selection = new VariablesSelection(); @@ -28,6 +28,12 @@ public SelectionResponse Map(Selection[] selections, PXMeta meta, string tableId ValueCodes = selection.ValueCodes.Cast().ToList(), }).ToList(); + response.Selection.Palcement = new VariablePlacementType() + { + Heading = heading, + Stub = stub + }; + response.Links = new List(); response.Links.Add(_linkCreator.GetDefaultSelectionLink(LinkCreator.LinkRelationEnum.self, tableId, lang, true)); diff --git a/PxWeb/Mappers/TableMetadataResponseMapper.cs b/PxWeb/Mappers/TableMetadataResponseMapper.cs index 6e0403bc..45fcd924 100644 --- a/PxWeb/Mappers/TableMetadataResponseMapper.cs +++ b/PxWeb/Mappers/TableMetadataResponseMapper.cs @@ -292,7 +292,12 @@ private void MapContact(TableMetadataResponse tm, PCAxis.Paxiom.Contact contact, } // Only display unique contact once - if (!tm.Contacts.Exists(x => x.Mail.Equals(c.Mail) && x.Name.Equals(c.Name) && x.Phone.Equals(c.Phone))) + if (!tm.Contacts.Exists(x => x.Mail is not null && + x.Name is not null && + x.Phone is not null && + x.Mail.Equals(c.Mail) && + x.Name.Equals(c.Name) && + x.Phone.Equals(c.Phone))) { tm.Contacts.Add(c); } diff --git a/PxWeb/Models/Api2/DatasetSubclass.cs b/PxWeb/Models/Api2/DatasetSubclass.cs index 1cf50f41..e4ae0c75 100644 --- a/PxWeb/Models/Api2/DatasetSubclass.cs +++ b/PxWeb/Models/Api2/DatasetSubclass.cs @@ -19,6 +19,11 @@ public DatasetSubclass() public void AddToTimeRole(string variableCode) { + if (Role is null) + { + Role = new DatasetRole(); + } + if (Role.Time == null) { Role.Time = new List(); @@ -29,6 +34,10 @@ public void AddToTimeRole(string variableCode) public void AddToMetricRole(string variableCode) { + if (Role is null) + { + Role = new DatasetRole(); + } if (Role.Metric == null) { Role.Metric = new List(); @@ -39,6 +48,10 @@ public void AddToMetricRole(string variableCode) public void AddToGeoRole(string variableCode) { + if (Role is null) + { + Role = new DatasetRole(); + } if (Role.Geo == null) { Role.Geo = new List(); @@ -51,6 +64,16 @@ public void AddInfoFile(string infoFile) { if (infoFile != null) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.Infofile = infoFile; } } @@ -59,6 +82,16 @@ public void AddTableId(string tableId) { if (tableId != null) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.Tableid = tableId; } } @@ -67,6 +100,15 @@ public void AddDecimals(int decimals) { if (decimals != -1) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } Extension.Px.Decimals = decimals; } } @@ -75,6 +117,16 @@ public void AddLanguage(string language) { if (language != null) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.Language = language; } } @@ -83,6 +135,16 @@ public void AddContents(string contents) { if (contents != null) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.Contents = contents; } } @@ -91,6 +153,16 @@ public void AddHeading(List heading) { if (heading != null) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.Heading = heading; } } @@ -98,11 +170,31 @@ public void AddStub(List stub) { if (stub != null) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.Stub = stub; } } public void AddOfficialStatistics(bool isOfficialStatistics) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.OfficialStatistics = isOfficialStatistics; } @@ -110,6 +202,16 @@ public void AddMatrix(string matrix) { if (matrix != null) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.Matrix = matrix; } } @@ -118,6 +220,15 @@ public void AddSubjectCode(string subjectCode) { if (subjectCode != null) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } Extension.Px.SubjectCode = subjectCode; } } @@ -126,12 +237,31 @@ public void AddSubjectArea(string subjectArea) { if (subjectArea != null) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.SubjectArea = subjectArea; } } public void AddAggRegAllowed(bool isAggRegAllowed) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } Extension.Px.Aggregallowed = isAggRegAllowed; } @@ -139,12 +269,32 @@ public void AddDescription(string description) { if (description != null) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.Description = description; } } public void AddDescriptiondefault(bool isDescriptiondefault) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + + if (Extension.Px is null) + { + Extension.Px = new ExtensionRootPx(); + } + Extension.Px.Descriptiondefault = isDescriptiondefault; } @@ -175,6 +325,11 @@ public void AddTableNote(string text) public void AddIsMandatoryForTableNote(string index) { + if (Extension is null) + { + Extension = new ExtensionRoot(); + } + if (Extension.NoteMandatory == null) Extension.NoteMandatory = new Dictionary(); Extension.NoteMandatory.Add(index, true); @@ -206,6 +361,11 @@ public void AddNoteToDimension(DatasetDimensionValue dimensionValue, string text public void AddIsMandatoryForDimensionNote(DatasetDimensionValue dimensionValue, string index) { + if (dimensionValue.Extension is null) + { + dimensionValue.Extension = new ExtensionDimension(); + } + if (dimensionValue.Extension.NoteMandatory == null) dimensionValue.Extension.NoteMandatory = new Dictionary(); dimensionValue.Extension.NoteMandatory.Add(index, true); @@ -213,6 +373,7 @@ public void AddIsMandatoryForDimensionNote(DatasetDimensionValue dimensionValue, public void AddValueNoteToCategory(DatasetDimensionValue dimensionValue, string valueNoteKey, string text) { + if (dimensionValue.Category is null) { dimensionValue.Category = new JsonstatCategory(); } if (dimensionValue.Category.Note == null) dimensionValue.Category.Note = new Dictionary>(); if (dimensionValue.Category.Note.ContainsKey(valueNoteKey)) @@ -229,6 +390,10 @@ public void AddValueNoteToCategory(DatasetDimensionValue dimensionValue, string public void AddIsMandatoryForCategoryNote(DatasetDimensionValue dimensionValue, string valueNoteKey, string index) { + if (dimensionValue.Extension is null) + { + dimensionValue.Extension = new ExtensionDimension(); + } if (dimensionValue.Extension.CategoryNoteMandatory == null) dimensionValue.Extension.CategoryNoteMandatory = new Dictionary>(); if (dimensionValue.Extension.CategoryNoteMandatory.ContainsKey(valueNoteKey)) @@ -254,6 +419,11 @@ public void AddRefPeriod(DatasetDimensionValue dimensionValue, string valueCode, { if (refPeriod == null) return; + if (dimensionValue.Extension is null) + { + dimensionValue.Extension = new ExtensionDimension(); + } + if (dimensionValue.Extension.Refperiod == null) dimensionValue.Extension.Refperiod = new Dictionary(); @@ -271,6 +441,10 @@ public void AddDimensionLink(DatasetDimensionValue dimensionValue, Dictionary codeLists) { + if (dimensionValue.Extension is null) + { + dimensionValue.Extension = new ExtensionDimension(); + } if (dimensionValue.Extension.CodeLists == null) { dimensionValue.Extension.CodeLists = new List(); diff --git a/PxWeb/PxWeb.csproj b/PxWeb/PxWeb.csproj index 6ddeca03..f0031ad0 100644 --- a/PxWeb/PxWeb.csproj +++ b/PxWeb/PxWeb.csproj @@ -51,7 +51,7 @@ - + From 613515f59f7e087d23df1da52ac59fe181f78a35 Mon Sep 17 00:00:00 2001 From: likp Date: Tue, 15 Oct 2024 15:07:33 +0200 Subject: [PATCH 2/8] Removed old DefaultSelection algorithm and associated tests --- PxWeb.UnitTests/Data/DataSelectionTest.cs | 306 +----------------- .../Api2/DataSelection/SelectionHandler.cs | 213 +----------- 2 files changed, 5 insertions(+), 514 deletions(-) diff --git a/PxWeb.UnitTests/Data/DataSelectionTest.cs b/PxWeb.UnitTests/Data/DataSelectionTest.cs index 0a4fcd98..e08664d7 100644 --- a/PxWeb.UnitTests/Data/DataSelectionTest.cs +++ b/PxWeb.UnitTests/Data/DataSelectionTest.cs @@ -5,213 +5,7 @@ namespace PxWeb.UnitTests.Data [TestClass] public class DataSelectionTest { - [TestMethod] - public void ShouldReturnSelectionFor_Time_Content() - { - PXModel model = GetPxModelDefaultSelection(2, 2); // Only Time and content cannot be eliminated. Content has 2 values - VariablesSelection variablesSelection = new VariablesSelection(); - variablesSelection.Selection = new List(); - SelectionHandler selectionHandler = new SelectionHandler(GetConfigMock().Object); - - Problem? problem; - var builderMock = new Mock(); - builderMock.Setup(x => x.Model).Returns(model); - Selection[]? selections = selectionHandler.GetSelection(builderMock.Object, variablesSelection, out problem); - - if (selections != null) - { - Selection? selection = selections.FirstOrDefault(s => s.VariableCode == "Period"); - if (selection != null) - { - Assert.AreEqual(12, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "Content"); - if (selection != null) - { - Assert.AreEqual(2, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var3"); - if (selection != null) - { - Assert.AreEqual(0, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var4"); - if (selection != null) - { - Assert.AreEqual(0, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var5"); - if (selection != null) - { - Assert.AreEqual(0, selection.ValueCodes.Count); - } - } - else - { - Assert.Fail(); - } - } - - [TestMethod] - public void ShouldReturnSelectionFor_Time_ContentWithOneValue_OneMore() - { - PXModel model = GetPxModelDefaultSelection(3, 1); // Time, content and a third variable cannot be eliminated. Content has 1 value - VariablesSelection variablesSelection = new VariablesSelection(); - variablesSelection.Selection = new List(); - SelectionHandler selectionHandler = new SelectionHandler(GetConfigMock().Object); - - Problem? problem; - var builderMock = new Mock(); - builderMock.Setup(x => x.Model).Returns(model); - Selection[]? selections = selectionHandler.GetSelection(builderMock.Object, variablesSelection, out problem); - - if (selections != null) - { - Selection? selection = selections.FirstOrDefault(s => s.VariableCode == "Period"); - if (selection != null) - { - Assert.AreEqual(12, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "Content"); - if (selection != null) - { - Assert.AreEqual(1, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var3"); - if (selection != null) - { - Assert.AreEqual(3, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var4"); - if (selection != null) - { - Assert.AreEqual(0, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var5"); - if (selection != null) - { - Assert.AreEqual(0, selection.ValueCodes.Count); - } - } - else - { - Assert.Fail(); - } - - } - - [TestMethod] - public void ShouldReturnSelectionFor_Time_ContentWithTwoValues_OneMore() - { - PXModel model = GetPxModelDefaultSelection(3, 2); // Time, content and a third variable cannot be eliminated. Content has 2 values - VariablesSelection variablesSelection = new VariablesSelection(); - variablesSelection.Selection = new List(); - SelectionHandler selectionHandler = new SelectionHandler(GetConfigMock().Object); - - Problem? problem; - var builderMock = new Mock(); - builderMock.Setup(x => x.Model).Returns(model); - Selection[]? selections = selectionHandler.GetSelection(builderMock.Object, variablesSelection, out problem); - - if (selections != null) - { - Selection? selection = selections.FirstOrDefault(s => s.VariableCode == "Period"); - if (selection != null) - { - Assert.AreEqual(1, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "Content"); - if (selection != null) - { - Assert.AreEqual(2, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var3"); - if (selection != null) - { - Assert.AreEqual(3, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var4"); - if (selection != null) - { - Assert.AreEqual(0, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var5"); - if (selection != null) - { - Assert.AreEqual(0, selection.ValueCodes.Count); - } - } - else - { - Assert.Fail(); - } - - } - - [TestMethod] - public void ShouldReturnSelectionFor_AllVariables() - { - PXModel model = GetPxModelDefaultSelection(5, 2); // No variable can be eliminated. Content has 2 values - VariablesSelection variablesSelection = new VariablesSelection(); - variablesSelection.Selection = new List(); - SelectionHandler selectionHandler = new SelectionHandler(GetConfigMock().Object); - - Problem? problem; - var builderMock = new Mock(); - builderMock.Setup(x => x.Model).Returns(model); - Selection[]? selections = selectionHandler.GetSelection(builderMock.Object, variablesSelection, out problem); - - if (selections != null) - { - Selection? selection = selections.FirstOrDefault(s => s.VariableCode == "Period"); - if (selection != null) - { - Assert.AreEqual(1, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "Content"); - if (selection != null) - { - Assert.AreEqual(1, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var3"); - if (selection != null) - { - Assert.AreEqual(3, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var4"); - if (selection != null) - { - Assert.AreEqual(3, selection.ValueCodes.Count); - } - - selection = selections.FirstOrDefault(s => s.VariableCode == "var5"); - if (selection != null) - { - Assert.AreEqual(1, selection.ValueCodes.Count); - } - } - else - { - Assert.Fail(); - } - - } - + [TestMethod] public void ShouldReturnWildcardStarSelection() { @@ -422,103 +216,7 @@ public void ShouldReturnToSelection() // Helper methods - - - private PXModel GetPxModelDefaultSelection(int nonElimVariables, int contentValues) - { - PXModel pxModel = new PXModel(); - - // Time - Variable timeVar = new Variable("Period", PlacementType.Heading); - timeVar.IsTime = true; - timeVar.Elimination = false; - - timeVar.Values.Add(CreateValue("2017M10")); - timeVar.Values.Add(CreateValue("2017M11")); - timeVar.Values.Add(CreateValue("2017M12")); - timeVar.Values.Add(CreateValue("2018M01")); - timeVar.Values.Add(CreateValue("2018M02")); - timeVar.Values.Add(CreateValue("2018M03")); - timeVar.Values.Add(CreateValue("2018M04")); - timeVar.Values.Add(CreateValue("2018M05")); - timeVar.Values.Add(CreateValue("2018M06")); - timeVar.Values.Add(CreateValue("2018M07")); - timeVar.Values.Add(CreateValue("2018M08")); - timeVar.Values.Add(CreateValue("2018M09")); - timeVar.Values.Add(CreateValue("2018M10")); - timeVar.Values.Add(CreateValue("2018M11")); - timeVar.Values.Add(CreateValue("2018M12")); - - pxModel.Meta.AddVariable(timeVar); - - // Content - Variable contentVar = new Variable("Content", PlacementType.Stub); - contentVar.IsContentVariable = true; - contentVar.Elimination = false; - - contentVar.Values.Add(CreateValue("CONTENT1")); - if (contentValues > 1) - { - contentVar.Values.Add(CreateValue("CONTENT2")); - } - - pxModel.Meta.AddVariable(contentVar); - - - // Variable 3 - Variable var3 = new Variable("var3", PlacementType.Stub); - if (nonElimVariables == 2) - { - var3.Elimination = true; - } - else - { - var3.Elimination = false; - } - - var3.Values.Add(CreateValue("Value1")); - var3.Values.Add(CreateValue("Value2")); - var3.Values.Add(CreateValue("Value3")); - - pxModel.Meta.AddVariable(var3); - - // Variable 4 - Variable var4 = new Variable("var4", PlacementType.Stub); - if (nonElimVariables == 2 || nonElimVariables == 3) - { - var4.Elimination = true; - } - else - { - var4.Elimination = false; - } - - var4.Values.Add(CreateValue("ValueA")); - var4.Values.Add(CreateValue("ValueB")); - var4.Values.Add(CreateValue("ValueC")); - - pxModel.Meta.AddVariable(var4); - - // Variable 5 - Variable var5 = new Variable("var5", PlacementType.Stub); - if (nonElimVariables < 4) - { - var5.Elimination = true; - } - else - { - var5.Elimination = false; - } - - var5.Values.Add(CreateValue("ValXXXX")); - var5.Values.Add(CreateValue("ValXXXY")); - var5.Values.Add(CreateValue("ValXXXZ")); - var5.Values.Add(CreateValue("ValXXXZ")); - - pxModel.Meta.AddVariable(var5); - return pxModel; - } - + private PCAxis.Paxiom.Value CreateValue(string code) { PCAxis.Paxiom.Value value = new PCAxis.Paxiom.Value(code); diff --git a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs index 35be8e89..3096646b 100644 --- a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs +++ b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs @@ -62,7 +62,8 @@ public SelectionHandler(IPxApiConfigurationService configOptionsService) } else { - selections = GetDefaultSelection(builder.Model); + problem = ProblemUtility.IllegalSelection(); + return null; } //Verify that valid selections could be made for mandatory variables @@ -206,10 +207,6 @@ private bool ApplyGrouping(IPXModelBuilder builder, Variable pxVariable, Variabl GroupingIncludesType include = GroupingIncludesType.AggregatedValues; // Always build for aggregated values - //if (variable.OutputValues == CodeListOutputValuesType.SingleEnum) - //{ - // include = GroupingIncludesType.SingleValues; - //} try { @@ -1028,205 +1025,7 @@ private bool GetSingleCode(string expression, out string code) } } - /// - /// Get the default selection based on an algorithm - /// - /// - /// - private Selection[] GetDefaultSelection(PXModel model) - { - // Only select from variables that cannot be eliminated - var variablesWithSelection = model.Meta.Variables.FindAll(v => v.Elimination == false); - - // Find the time variable - var timeVar = variablesWithSelection.Find(v => v.IsTime == true); - - // Find the contents variable - var contentVar = variablesWithSelection.Find(v => v.IsContentVariable == true); - - // 2 variables - Time and Content - if (variablesWithSelection.Count == 2 && timeVar != null && contentVar != null) - { - return GetDefaultSelectionOnlyTimeAndContent(model); - } - // 3 variables - Time, Content (with only 1 value) and one more variable - else if (variablesWithSelection.Count == 3 && timeVar != null && contentVar != null && contentVar.Values.Count == 1) - { - return GetDefaultSelectionThreeVariablesTimeAndContentOneValue(model); - } - // 3 variables - Time, Content (with more than 1 value) and one more variable - else if (variablesWithSelection.Count == 3 && timeVar != null && contentVar != null && contentVar.Values.Count > 1) - { - return GetDefaultSelectionThreeVariablesTimeAndContentMoreThanOneValue(model); - } - // All other cases - else - { - return GetDefaultSelectionAllOtherCases(model); - } - } - - /// - /// Returns default selection when only the time- and contentvariable cannot be eliminated - /// - /// - /// - private Selection[] GetDefaultSelectionOnlyTimeAndContent(PXModel model) - { - var selections = new List(); - - foreach (var variable in model.Meta.Variables) - { - var selection = new Selection(variable.Code); - - if (variable.IsContentVariable) - { - // Content - Takes all values - selection.ValueCodes.AddRange(GetAllCodes(variable)); - } - else if (variable.IsTime) - { - // Time - Take the 12 lastNoneMandantoryClassificationVariable values - selection.ValueCodes.AddRange(GetTimeCodes(variable, 12)); - } - else - { - // Select nothing for the other variables - } - - selections.Add(selection); - } - - return selections.ToArray(); - } - - /// - /// Returns default selection when the time-, the content variable and one more variable cannot be eliminated. - /// Also the content variable only has one value. - /// - /// - /// - private Selection[] GetDefaultSelectionThreeVariablesTimeAndContentOneValue(PXModel model) - { - var selections = new List(); - - foreach (var variable in model.Meta.Variables) - { - var selection = new Selection(variable.Code); - - if (variable.IsContentVariable) - { - // Content - Take the one value - selection.ValueCodes.AddRange(GetCodes(variable, 1)); - } - else if (variable.IsTime) - { - // Time - Take the 12 lastNoneMandantoryClassificationVariable values - selection.ValueCodes.AddRange(GetTimeCodes(variable, 12)); - } - else if (!variable.Elimination) - { - // Take all values for the third variable - selection.ValueCodes.AddRange(GetAllCodes(variable)); - } - else - { - // Select nothing for the other variables - } - - selections.Add(selection); - } - - return selections.ToArray(); - } - - /// - /// Returns default selection when the time-, the content variable and one more variable cannot be eliminated. - /// Also the content variable only has more than one value. - /// - /// - /// - private Selection[] GetDefaultSelectionThreeVariablesTimeAndContentMoreThanOneValue(PXModel model) - { - var selections = new List(); - - foreach (var variable in model.Meta.Variables) - { - var selection = new Selection(variable.Code); - - if (variable.IsContentVariable) - { - // Content - Take all values - selection.ValueCodes.AddRange(GetAllCodes(variable)); - } - else if (variable.IsTime) - { - // Time - Take the lastNoneMandantoryClassificationVariable value - selection.ValueCodes.AddRange(GetTimeCodes(variable, 1)); - } - else if (!variable.Elimination) - { - // Take all values for the third variable - selection.ValueCodes.AddRange(GetAllCodes(variable)); - } - else - { - // Select nothing for the other variables - } - - selections.Add(selection); - } - - return selections.ToArray(); - } - - /// - /// Returns default selection for all other cases - /// - /// - /// - private Selection[] GetDefaultSelectionAllOtherCases(PXModel model) - { - var selections = new List(); - int variableNumber = 1; - - foreach (var variable in model.Meta.Variables) - { - var selection = new Selection(variable.Code); - - if (variable.IsContentVariable) - { - // Content - Take the first value - selection.ValueCodes.AddRange(GetCodes(variable, 1)); - } - else if (variable.IsTime) - { - // Time - Take the lastNoneMandantoryClassificationVariable value - selection.ValueCodes.AddRange(GetTimeCodes(variable, 1)); - } - else if (!variable.Elimination && (variableNumber == 1 || variableNumber == 2)) - { - // Take all values for variable 1 and 2 - selection.ValueCodes.AddRange(GetAllCodes(variable)); - variableNumber++; - } - else if (!variable.Elimination && (variableNumber > 2)) - { - // Take 1 value - selection.ValueCodes.AddRange(GetCodes(variable, 1)); - variableNumber++; - } - else - { - // Select nothing for the other variables - } - - selections.Add(selection); - } - - return selections.ToArray(); - } - + private List GetAllMandatoryVariables(PXModel model) { var mandatoryVariables = model.Meta.Variables.Where(x => x.Elimination.Equals(false)).ToList(); @@ -1254,12 +1053,6 @@ private string[] GetCodes(Variable variable, int count) return codes; } - private string[] GetAllCodes(Variable variable) - { - var codes = variable.Values.Select(value => value.Code).ToArray(); - - return codes; - } private string[] GetTimeCodes(Variable variable, int count) { From 40feb81667f0bd91eb3489aa8194e3a9a9d200b1 Mon Sep 17 00:00:00 2001 From: likp Date: Tue, 15 Oct 2024 15:41:36 +0200 Subject: [PATCH 3/8] Refactored the default selection algorithm --- .../Api2/DataSelection/SelectionHandler.cs | 248 ++++++++++-------- 1 file changed, 145 insertions(+), 103 deletions(-) diff --git a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs index 3096646b..dccdb159 100644 --- a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs +++ b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs @@ -1259,145 +1259,187 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) private (List, List, List) GetDefaultSelectionByAlgorithm(PXMeta meta, Variable contents, Variable time) { - var selections = new List(); - List placmentHeading = new List(); - List placmentStub = new List(); - - if (meta.Variables.Count == 2) // Case A according to algorithm + if (meta.Variables.Count == 2) { - // PX table using good practice och CNMM datasource - if (contents.Values.Count < 6) - { - selections.AddHeadingVariable(contents, GetCodes); - selections.AddStubVariable(time, GetTimeCodes, 13); - placmentHeading.Add(contents.Code); - placmentStub.Add(time.Code); - } - else + // Case A according to algorithm + return OnlyContentsAndTime(contents, time); + } + else if (meta.Variables.Count == 3) + { + // Case B according to algorithm + var variable = meta.Variables.FirstOrDefault(v => v.Code != contents.Code && v.Code != time.Code); + if (variable is not null) { - selections.AddStubVariable(contents, GetCodes); - selections.AddHeadingVariable(time, GetTimeCodes, 13); - placmentHeading.Add(time.Code); - placmentStub.Add(contents.Code); + return WithThreeDimensions(contents, time, variable); } } - else if (meta.Variables.Count == 3) // Case B + else + { + // Case C according to algorithm + var classificationVariables = meta.Variables.Where(v => v.Code != contents.Code && v.Code != time.Code).ToList(); + var mandatoryClassificationVariables = classificationVariables.Where(v => v.Elimination == false).ToList(); + var noneMandatoryClassificationVariables = classificationVariables.Where(v => v.Elimination == true).ToList(); + return WithMoreThenTreeDimensions(contents, time, classificationVariables, mandatoryClassificationVariables, noneMandatoryClassificationVariables); + } + + return (new List(), new List(), new List()); + } + + private (List, List, List) WithMoreThenTreeDimensions(Variable contents, Variable time, List classificationVariables, List mandatoryClassificationVariables, List noneMandatoryClassificationVariables) + { + var selections = new List(); + List placmentHeading = new List(); + List placmentStub = new List(); + + //First content and lastNoneMandantoryClassificationVariable time period + selections.AddVariableToHeading(contents, GetCodes); + selections.AddVariableToHeading(time, GetTimeCodes); + placmentHeading.Add(contents.Code); + + if (mandatoryClassificationVariables.Count > 1) { - if (contents.Values.Count == 1) + for (int i = 1; i < (mandatoryClassificationVariables.Count - 1); i++) { - // select the contents and 13 latest time values - selections.AddVariableToHeading(contents, GetCodes); - selections.AddHeadingVariable(time, GetTimeCodes, 13); - placmentHeading.Add(contents.Code); - placmentHeading.Add(time.Code); - - var variable = meta.Variables.FirstOrDefault(v => v.Code != contents.Code && v.Code != time.Code); - if (variable != null) - { - selections.AddStubVariable(variable, GetCodes); - placmentStub.Add(variable.Code); - } + selections.AddVariableToHeading(mandatoryClassificationVariables[i], GetCodes); + placmentHeading.Add(mandatoryClassificationVariables[i].Code); } - else - { - var variable = meta.Variables.FirstOrDefault(v => v.Code != contents.Code && v.Code != time.Code); - if (variable is null) - { - throw new Exception("Douplicate time or contents variables"); - } - //Add the latest time value - selections.AddVariableToHeading(time, GetTimeCodes); - placmentHeading.Add(time.Code); + //The variable with the most values should be in the placmentHeading + var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], mandatoryClassificationVariables[mandatoryClassificationVariables.Count - 1]); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); - //Check if contents of classification should be in placmentStub or placmentHeading - var (stub, heading) = StubOrHeading(contents, variable); - selections.AddStubVariable(stub, GetCodes); - selections.AddHeadingVariable(heading, GetCodes); + selections.AddStubVariable(stub, GetCodes); + selections.AddHeadingVariable(heading, GetCodes); - placmentHeading.Add(heading.Code); - placmentStub.Add(stub.Code); + //Add the none mandatory classification variables without any selected values + foreach (var variable in noneMandatoryClassificationVariables) + { + selections.EliminateVariable(variable); } } - else // Case C + else if (mandatoryClassificationVariables.Count == 1) { - //First content and lastNoneMandantoryClassificationVariable time period - selections.AddVariableToHeading(contents, GetCodes); - selections.AddVariableToHeading(time, GetTimeCodes); - placmentHeading.Add(contents.Code); + var lastNoneMandantoryClassificationVariable = noneMandatoryClassificationVariables.Last(); + var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], lastNoneMandantoryClassificationVariable); + selections.AddStubVariable(stub, GetCodes); + selections.AddHeadingVariable(heading, GetCodes); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); - var classificationVariables = meta.Variables.Where(v => v.Code != contents.Code && v.Code != time.Code).ToList(); - var mandatoryClassificationVariables = classificationVariables.Where(v => v.Elimination == false).ToList(); - var noneMandatoryClassificationVariables = classificationVariables.Where(v => v.Elimination == true).ToList(); - if (mandatoryClassificationVariables.Count > 1) + foreach (var variable in noneMandatoryClassificationVariables) { - for (int i = 1; i < (mandatoryClassificationVariables.Count - 1); i++) + if (variable != lastNoneMandantoryClassificationVariable) { - selections.AddVariableToHeading(mandatoryClassificationVariables[i], GetCodes); - placmentHeading.Add(mandatoryClassificationVariables[i].Code); + selections.EliminateVariable(variable); } + } + } + else //No mandatory classification variables + { + var firstNoneMandantoryClassificationVariable = classificationVariables.First(); + var lastNoneMandantoryClassificationVariable = classificationVariables.Last(); + var (stub, heading) = StubOrHeading(firstNoneMandantoryClassificationVariable, lastNoneMandantoryClassificationVariable); + selections.AddStubVariable(stub, GetCodes); + selections.AddHeadingVariable(heading, GetCodes); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); - //The variable with the most values should be in the placmentHeading - var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], mandatoryClassificationVariables[mandatoryClassificationVariables.Count - 1]); - placmentHeading.Add(heading.Code); - placmentStub.Add(stub.Code); - - selections.AddStubVariable(stub, GetCodes); - selections.AddHeadingVariable(heading, GetCodes); - //Add the none mandatory classification variables without any selected values - foreach (var variable in noneMandatoryClassificationVariables) + foreach (var variable in noneMandatoryClassificationVariables) + { + if (variable != firstNoneMandantoryClassificationVariable && variable != lastNoneMandantoryClassificationVariable) { selections.EliminateVariable(variable); } } - else if (mandatoryClassificationVariables.Count == 1) - { - var lastNoneMandantoryClassificationVariable = noneMandatoryClassificationVariables.Last(); - var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], lastNoneMandantoryClassificationVariable); - selections.AddStubVariable(stub, GetCodes); - selections.AddHeadingVariable(heading, GetCodes); + } + //place time as last variable in heading + placmentHeading.Add(time.Code); + return (selections, placmentHeading, placmentStub); + } - placmentHeading.Add(heading.Code); - placmentStub.Add(stub.Code); + /// + /// Case B according to the algorithm when three variables are present and where contents and time are two of them + /// + /// + /// + /// + /// + /// + private (List, List, List) WithThreeDimensions(Variable contents, Variable time, Variable variable) + { + var selections = new List(); + List placmentHeading = new List(); + List placmentStub = new List(); + if (contents.Values.Count == 1) + { + // select the contents and 13 latest time values + selections.AddVariableToHeading(contents, GetCodes); + selections.AddHeadingVariable(time, GetTimeCodes, 13); + placmentHeading.Add(contents.Code); + placmentHeading.Add(time.Code); + selections.AddStubVariable(variable, GetCodes); + placmentStub.Add(variable.Code); - foreach (var variable in noneMandatoryClassificationVariables) - { - if (variable != lastNoneMandantoryClassificationVariable) - { - selections.EliminateVariable(variable); - } - } - } - else //No mandatory classification variables - { - var firstNoneMandantoryClassificationVariable = classificationVariables.First(); - var lastNoneMandantoryClassificationVariable = classificationVariables.Last(); - var (stub, heading) = StubOrHeading(firstNoneMandantoryClassificationVariable, lastNoneMandantoryClassificationVariable); - selections.AddStubVariable(stub, GetCodes); - selections.AddHeadingVariable(heading, GetCodes); - placmentHeading.Add(heading.Code); - placmentStub.Add(stub.Code); + } + else + { + //Add the latest time value + selections.AddVariableToHeading(time, GetTimeCodes); + placmentHeading.Add(time.Code); + //Check if contents of classification should be in placmentStub or placmentHeading + var (stub, heading) = StubOrHeading(contents, variable); + selections.AddStubVariable(stub, GetCodes); + selections.AddHeadingVariable(heading, GetCodes); - foreach (var variable in noneMandatoryClassificationVariables) - { - if (variable != firstNoneMandantoryClassificationVariable && variable != lastNoneMandantoryClassificationVariable) - { - selections.EliminateVariable(variable); - } - } - } - //place time as last variable in heading + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); + } + + return (selections, placmentHeading, placmentStub); + } + + /// + /// Case A according to the algorithm when oly contents and time variables are present + /// + /// + /// + /// + private (List, List, List) OnlyContentsAndTime(Variable contents, Variable time) + { + var selections = new List(); + List placmentHeading = new List(); + List placmentStub = new List(); + + if (contents.Values.Count < 6) + { + selections.AddHeadingVariable(contents, GetCodes); + selections.AddStubVariable(time, GetTimeCodes, 13); + placmentHeading.Add(contents.Code); + placmentStub.Add(time.Code); + } + else + { + selections.AddStubVariable(contents, GetCodes); + selections.AddHeadingVariable(time, GetTimeCodes, 13); placmentHeading.Add(time.Code); + placmentStub.Add(contents.Code); } return (selections, placmentHeading, placmentStub); } + /// + /// Helper function that determis which variable should to the Stub or Heading + /// + /// first variable + /// second variable + /// variable that should go to the stub and the variable hat should go to the heading private static (Variable, Variable) StubOrHeading(Variable one, Variable two) { if (one.Values.Count > two.Values.Count) From 96085f684cc6ba761fb211de8d3468493aeac2a0 Mon Sep 17 00:00:00 2001 From: likp Date: Tue, 15 Oct 2024 15:43:08 +0200 Subject: [PATCH 4/8] Added comment --- PxWeb/Code/Api2/DataSelection/SelectionHandler.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs index dccdb159..9d7334f4 100644 --- a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs +++ b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs @@ -1285,6 +1285,15 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) return (new List(), new List(), new List()); } + /// + /// Case C according to the algorithm when more then three variables are present and where contents and time are two of them + /// + /// + /// + /// + /// + /// + /// private (List, List, List) WithMoreThenTreeDimensions(Variable contents, Variable time, List classificationVariables, List mandatoryClassificationVariables, List noneMandatoryClassificationVariables) { var selections = new List(); From ca33f003b06106cf1eb332c6b699b56cf1403c77 Mon Sep 17 00:00:00 2001 From: likp Date: Tue, 15 Oct 2024 15:45:13 +0200 Subject: [PATCH 5/8] Added more comments --- .../Api2/DataSelection/SelectionHandler.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs index 9d7334f4..773f27aa 100644 --- a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs +++ b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs @@ -1134,6 +1134,12 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) return variablesSelection is null || !HasSelection(variablesSelection); } + /// + /// Gets the default selection for a table + /// + /// + /// + /// public (Selection[]?, List, List) GetDefaultSelection(IPXModelBuilder builder, out Problem? problem) { var meta = builder.Model.Meta; @@ -1181,6 +1187,11 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) } + /// + /// Fallback method for getting default selection when contents and time are not present + /// + /// + /// private (List, List, List) GetDefaultSelectionByAlgorithmFallback(PXMeta meta) { var selections = new List(); @@ -1257,6 +1268,13 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) return (selections, placmentHeading, placmentStub); } + /// + /// Gets the default selection by the algorithm when both contents and time are present + /// + /// + /// + /// + /// private (List, List, List) GetDefaultSelectionByAlgorithm(PXMeta meta, Variable contents, Variable time) { if (meta.Variables.Count == 2) From 4d4880690b8c50a98d4daf2bab317f7dcd749717 Mon Sep 17 00:00:00 2001 From: likp Date: Tue, 15 Oct 2024 15:46:49 +0200 Subject: [PATCH 6/8] Ran code clean up --- PxWeb/Code/Api2/DataSelection/SelectionHandler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs index 773f27aa..5df0c73f 100644 --- a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs +++ b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs @@ -1025,7 +1025,7 @@ private bool GetSingleCode(string expression, out string code) } } - + private List GetAllMandatoryVariables(PXModel model) { var mandatoryVariables = model.Meta.Variables.Where(x => x.Elimination.Equals(false)).ToList(); @@ -1277,12 +1277,12 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) /// private (List, List, List) GetDefaultSelectionByAlgorithm(PXMeta meta, Variable contents, Variable time) { - if (meta.Variables.Count == 2) + if (meta.Variables.Count == 2) { // Case A according to algorithm return OnlyContentsAndTime(contents, time); } - else if (meta.Variables.Count == 3) + else if (meta.Variables.Count == 3) { // Case B according to algorithm var variable = meta.Variables.FirstOrDefault(v => v.Code != contents.Code && v.Code != time.Code); @@ -1291,7 +1291,7 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) return WithThreeDimensions(contents, time, variable); } } - else + else { // Case C according to algorithm var classificationVariables = meta.Variables.Where(v => v.Code != contents.Code && v.Code != time.Code).ToList(); @@ -1428,7 +1428,7 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) placmentStub.Add(stub.Code); } - return (selections, placmentHeading, placmentStub); + return (selections, placmentHeading, placmentStub); } /// From bbef085b7ee70c3dba8c53ef679d09ddc60aa43c Mon Sep 17 00:00:00 2001 From: likp Date: Tue, 15 Oct 2024 15:57:55 +0200 Subject: [PATCH 7/8] misst commiting test file --- PxWeb.UnitTests/Data/DataSelectionTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PxWeb.UnitTests/Data/DataSelectionTest.cs b/PxWeb.UnitTests/Data/DataSelectionTest.cs index e08664d7..d4fb55ba 100644 --- a/PxWeb.UnitTests/Data/DataSelectionTest.cs +++ b/PxWeb.UnitTests/Data/DataSelectionTest.cs @@ -5,7 +5,7 @@ namespace PxWeb.UnitTests.Data [TestClass] public class DataSelectionTest { - + [TestMethod] public void ShouldReturnWildcardStarSelection() { @@ -216,7 +216,7 @@ public void ShouldReturnToSelection() // Helper methods - + private PCAxis.Paxiom.Value CreateValue(string code) { PCAxis.Paxiom.Value value = new PCAxis.Paxiom.Value(code); From 1155ee3f26b267ded7b5bb3e728ae2e103d7f019 Mon Sep 17 00:00:00 2001 From: likp Date: Tue, 15 Oct 2024 16:30:34 +0200 Subject: [PATCH 8/8] More refactoring on Case C in the default algorithm --- .../Api2/DataSelection/SelectionHandler.cs | 154 +++++++++++++----- 1 file changed, 110 insertions(+), 44 deletions(-) diff --git a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs index 5df0c73f..4c218154 100644 --- a/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs +++ b/PxWeb/Code/Api2/DataSelection/SelectionHandler.cs @@ -1313,6 +1313,31 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) /// /// private (List, List, List) WithMoreThenTreeDimensions(Variable contents, Variable time, List classificationVariables, List mandatoryClassificationVariables, List noneMandatoryClassificationVariables) + { + + if (mandatoryClassificationVariables.Count > 1) + { + return WithContentsAndTimeAndMoreThenOneMandatoryClassificationVariables(contents, time, mandatoryClassificationVariables, noneMandatoryClassificationVariables); + } + else if (mandatoryClassificationVariables.Count == 1) + { + return WithContentsAndTimeAndOneMandatoryClassificationVariables(contents, time, mandatoryClassificationVariables, noneMandatoryClassificationVariables); + } + + return WithContentsAndTimeAndNoMandatoryClassificationVariables(contents, time, classificationVariables, noneMandatoryClassificationVariables); + + } + + /// + /// Case C according to the algorithm when more then three variables are present and where contents and time are two of them + /// and more then one mandatory variable + /// + /// + /// + /// + /// + /// + private (List, List, List) WithContentsAndTimeAndMoreThenOneMandatoryClassificationVariables(Variable contents, Variable time, List mandatoryClassificationVariables, List noneMandatoryClassificationVariables) { var selections = new List(); List placmentHeading = new List(); @@ -1323,64 +1348,105 @@ public bool UseDefaultSelection(VariablesSelection? variablesSelection) selections.AddVariableToHeading(time, GetTimeCodes); placmentHeading.Add(contents.Code); - if (mandatoryClassificationVariables.Count > 1) + for (int i = 1; i < (mandatoryClassificationVariables.Count - 1); i++) { - for (int i = 1; i < (mandatoryClassificationVariables.Count - 1); i++) - { - selections.AddVariableToHeading(mandatoryClassificationVariables[i], GetCodes); - placmentHeading.Add(mandatoryClassificationVariables[i].Code); - } + selections.AddVariableToHeading(mandatoryClassificationVariables[i], GetCodes); + placmentHeading.Add(mandatoryClassificationVariables[i].Code); + } - //The variable with the most values should be in the placmentHeading - var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], mandatoryClassificationVariables[mandatoryClassificationVariables.Count - 1]); - placmentHeading.Add(heading.Code); - placmentStub.Add(stub.Code); + //The variable with the most values should be in the placmentHeading + var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], mandatoryClassificationVariables[mandatoryClassificationVariables.Count - 1]); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); - selections.AddStubVariable(stub, GetCodes); - selections.AddHeadingVariable(heading, GetCodes); + selections.AddStubVariable(stub, GetCodes); + selections.AddHeadingVariable(heading, GetCodes); - //Add the none mandatory classification variables without any selected values - foreach (var variable in noneMandatoryClassificationVariables) - { - selections.EliminateVariable(variable); - } - } - else if (mandatoryClassificationVariables.Count == 1) + //Add the none mandatory classification variables without any selected values + foreach (var variable in noneMandatoryClassificationVariables) { - var lastNoneMandantoryClassificationVariable = noneMandatoryClassificationVariables.Last(); - var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], lastNoneMandantoryClassificationVariable); - selections.AddStubVariable(stub, GetCodes); - selections.AddHeadingVariable(heading, GetCodes); + selections.EliminateVariable(variable); + } - placmentHeading.Add(heading.Code); - placmentStub.Add(stub.Code); + //place time as last variable in heading + placmentHeading.Add(time.Code); + return (selections, placmentHeading, placmentStub); + } + /// + /// Case C according to the algorithm when more then three variables are present and where contents and time are two of them + /// and only one mandatory variable + /// + /// + /// + /// + /// + /// + private (List, List, List) WithContentsAndTimeAndOneMandatoryClassificationVariables(Variable contents, Variable time, List mandatoryClassificationVariables, List noneMandatoryClassificationVariables) + { + var selections = new List(); + List placmentHeading = new List(); + List placmentStub = new List(); + + //First content and lastNoneMandantoryClassificationVariable time period + selections.AddVariableToHeading(contents, GetCodes); + selections.AddVariableToHeading(time, GetTimeCodes); + placmentHeading.Add(contents.Code); + + var lastNoneMandantoryClassificationVariable = noneMandatoryClassificationVariables.Last(); + var (stub, heading) = StubOrHeading(mandatoryClassificationVariables[0], lastNoneMandantoryClassificationVariable); + selections.AddStubVariable(stub, GetCodes); + selections.AddHeadingVariable(heading, GetCodes); + + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); - foreach (var variable in noneMandatoryClassificationVariables) + foreach (var variable in noneMandatoryClassificationVariables) + { + if (variable != lastNoneMandantoryClassificationVariable) { - if (variable != lastNoneMandantoryClassificationVariable) - { - selections.EliminateVariable(variable); - } + selections.EliminateVariable(variable); } } - else //No mandatory classification variables - { - var firstNoneMandantoryClassificationVariable = classificationVariables.First(); - var lastNoneMandantoryClassificationVariable = classificationVariables.Last(); - var (stub, heading) = StubOrHeading(firstNoneMandantoryClassificationVariable, lastNoneMandantoryClassificationVariable); - selections.AddStubVariable(stub, GetCodes); - selections.AddHeadingVariable(heading, GetCodes); - placmentHeading.Add(heading.Code); - placmentStub.Add(stub.Code); + //place time as last variable in heading + placmentHeading.Add(time.Code); + return (selections, placmentHeading, placmentStub); + } + + /// + /// Case C according to the algorithm when more then three variables are present and where contents and time are two of them + /// and there are no mandatory classification variables + /// + /// + /// + /// + /// + /// + private (List, List, List) WithContentsAndTimeAndNoMandatoryClassificationVariables(Variable contents, Variable time, List classificationVariables, List noneMandatoryClassificationVariables) + { + var selections = new List(); + List placmentHeading = new List(); + List placmentStub = new List(); + + //First content and lastNoneMandantoryClassificationVariable time period + selections.AddVariableToHeading(contents, GetCodes); + selections.AddVariableToHeading(time, GetTimeCodes); + placmentHeading.Add(contents.Code); + + var firstNoneMandantoryClassificationVariable = classificationVariables.First(); + var lastNoneMandantoryClassificationVariable = classificationVariables.Last(); + var (stub, heading) = StubOrHeading(firstNoneMandantoryClassificationVariable, lastNoneMandantoryClassificationVariable); + selections.AddStubVariable(stub, GetCodes); + selections.AddHeadingVariable(heading, GetCodes); + placmentHeading.Add(heading.Code); + placmentStub.Add(stub.Code); - foreach (var variable in noneMandatoryClassificationVariables) + foreach (var variable in noneMandatoryClassificationVariables) + { + if (variable != firstNoneMandantoryClassificationVariable && variable != lastNoneMandantoryClassificationVariable) { - if (variable != firstNoneMandantoryClassificationVariable && variable != lastNoneMandantoryClassificationVariable) - { - selections.EliminateVariable(variable); - } + selections.EliminateVariable(variable); } } //place time as last variable in heading