From 664e3da37201f94b4970ca4fdad952b150cc00c6 Mon Sep 17 00:00:00 2001 From: JohannesFinsveen Date: Wed, 15 Apr 2026 12:48:34 +0200 Subject: [PATCH 1/4] bumped PxWeb.Api2.Server.Models and added new links to output --- .../JsonStat2/Model/JsonStat2Dataset.cs | 40 ++++++++ PCAxis.Serializers/JsonStat2Serializer.cs | 80 +++++++++++++++- PCAxis.Serializers/PCAxis.Serializers.csproj | 2 +- UnitTests/JsonStat2/MetaIdTest.cs | 91 +++++++++++++++++++ UnitTests/TestFiles/tab_12909_metaid.px | 77 ++++++++++++++++ 5 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 UnitTests/JsonStat2/MetaIdTest.cs create mode 100644 UnitTests/TestFiles/tab_12909_metaid.px diff --git a/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs b/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs index 70f2893..5956ed4 100644 --- a/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs +++ b/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs @@ -354,5 +354,45 @@ public static void AddBasePeriod(DimensionValue dimensionValue, string valueCode dimensionValue.Extension.BasePeriod.Add(valueCode, basePeriod); } } + + //On Dimension + public static void AddRelatedLink(DimensionValue dimensionValue, RelatedLink theLink) + { + InitializeRelated(dimensionValue); + dimensionValue.Link.Related.Add(theLink); + } + + //On root + public void AddRelatedLink(RelatedLink theLink) + { + InitiallizeRelated(); + this.Link.Related.Add(theLink); + } + + + //On Dimension + private static void InitializeRelated(DimensionValue dimensionValue) + { + if (dimensionValue.Link == null) + { + dimensionValue.Link = new JsonstatExtensionLink(); + } + if (dimensionValue.Link.Related == null) + { + dimensionValue.Link.Related = new List(); + } + } + + + + //On root + private void InitiallizeRelated() + { + if (this.Link == null) + { + this.Link = new JsonstatExtensionLink(); + this.Link.Related = new List(); + } + } } } diff --git a/PCAxis.Serializers/JsonStat2Serializer.cs b/PCAxis.Serializers/JsonStat2Serializer.cs index 62ca2b2..e707ffe 100644 --- a/PCAxis.Serializers/JsonStat2Serializer.cs +++ b/PCAxis.Serializers/JsonStat2Serializer.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Data; using System.Globalization; using System.IO; using System.Linq; +using System.Reflection.Emit; using System.Text; using Newtonsoft.Json; @@ -11,6 +13,7 @@ using PCAxis.Paxiom; using PCAxis.Paxiom.Extensions; using PCAxis.Serializers.JsonStat2.Model; +using PCAxis.Serializers.Util.MetaId; using PxWeb.Api2.Server.Models; @@ -41,6 +44,7 @@ private static Dictionary BuildDataSymbolMap(PXMeta meta) public string BuildJsonStructure(PXModel model) { var dataset = new JsonStat2Dataset(); + string language = model.Meta.CurrentLanguage; //Updated AddUpdated(model, dataset); @@ -51,6 +55,10 @@ public string BuildJsonStructure(PXModel model) //Label dataset.AddLabel(model.Meta.Title); + //Metaid handeling and output comes in OldWay and NewWay + //OldWay will hopfully end in 3.0. On root there is just the NewWay + AddMetaid(dataset, model.Meta.MetaId, language); + //Extension PX AddPxToExtension(model, dataset); @@ -64,7 +72,7 @@ public string BuildJsonStructure(PXModel model) foreach (var variable in model.Meta.Variables) { - //temporary collector storage + //temporary collector storage (For Old Way) var metaIdsHelper = new Dictionary(); dataset.AddDimensionValue(variable.Code, variable.Name, out var dimensionValue); @@ -79,8 +87,11 @@ public string BuildJsonStructure(PXModel model) dimensionValue.Category.Index.Add(variableValue.Code, indexCounter++); } + CollectMetaIdsForValue(variableValue, ref metaIdsHelper); + AddMetaidOnValue(dimensionValue, variable, variableValue, language); + // ValueNote AddValueNotes(variableValue, dimensionValue); @@ -133,12 +144,17 @@ public string BuildJsonStructure(PXModel model) AddVariableNotes(variable, dimensionValue); //MetaID + + //OldWay CollectMetaIdsForVariable(variable, ref metaIdsHelper); if (metaIdsHelper.Count > 0) { JsonStat2Dataset.AddDimensionLink(dimensionValue, metaIdsHelper); } + //NewWay + AddMetaidOnVariable(dimensionValue, variable, language); + dataset.Size.Add(variable.Values.Count); dataset.Id.Add(variable.Code); @@ -164,6 +180,68 @@ public string BuildJsonStructure(PXModel model) return result; } + private void AddMetaidOnVariable(DimensionValue dimensionValue, Variable variable, string language) + { + if (String.IsNullOrEmpty(variable.MetaId)){ + return; + } + + foreach (var metalink in MetaIdResolverStatic.GetVariableLinks(variable.MetaId, language, variable.Name)) + { + JsonStat2Dataset.AddRelatedLink(dimensionValue, ToRelatedLink(metalink, null)); + } + } + + private void AddMetaidOnValue(DimensionValue dimensionValue, Variable variable, Value variableValue, string language) + { + if (String.IsNullOrEmpty(variableValue.MetaId)) + { + return; + } + + // There are 3 props on a "Variable Value": Code + Value + Text (combo of Code and Value) + // + foreach (var metalink in MetaIdResolverStatic.GetValueLinks(variableValue.MetaId,language,variable.Name, variableValue.Text)) + { + JsonStat2Dataset.AddRelatedLink(dimensionValue, ToRelatedLink(metalink, variableValue.Code)); + } + } + + private void AddMetaid(JsonStat2Dataset dataset, string metaIdRaw, string language) + { + if (String.IsNullOrEmpty(metaIdRaw)) + { + return; + } + + foreach (Util.MetaId.Link metalink in MetaIdResolverStatic.GetTableLinks(metaIdRaw, language)) + { + dataset.AddRelatedLink(ToRelatedLink(metalink, null)); + } + + } + + + private static RelatedLink ToRelatedLink(Util.MetaId.Link metalink, string category) + { + RelatedLink myOut = new RelatedLink(); + myOut.Extension = new RelatedLinkExtension(); + myOut.Extension.Relation = metalink.Relation; + if (! String.IsNullOrEmpty(category )) + { + myOut.Extension.Category = category; + } + myOut.Extension.Metaid = metalink.MetaId; + + myOut.Href = metalink.Url; + myOut.Label = metalink.Label; + myOut.Type = metalink.Type; + + return myOut; + } + + + private static PriceType GetPriceType(string cfprices) { string cfp = cfprices != null ? cfprices.ToUpper() : ""; diff --git a/PCAxis.Serializers/PCAxis.Serializers.csproj b/PCAxis.Serializers/PCAxis.Serializers.csproj index 4ff45b7..36a502a 100644 --- a/PCAxis.Serializers/PCAxis.Serializers.csproj +++ b/PCAxis.Serializers/PCAxis.Serializers.csproj @@ -38,7 +38,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/UnitTests/JsonStat2/MetaIdTest.cs b/UnitTests/JsonStat2/MetaIdTest.cs new file mode 100644 index 0000000..c7b5231 --- /dev/null +++ b/UnitTests/JsonStat2/MetaIdTest.cs @@ -0,0 +1,91 @@ +using System.Globalization; +using System.Linq; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Newtonsoft.Json.Linq; + +using PCAxis.Paxiom; + +namespace UnitTests.JsonStat2 +{ + [TestClass] + [DeploymentItem("TestFiles/metaid.config")] + [DeploymentItem("TestFiles/tab_12909_metaid.px")] + + public class MetaIdTest + { + private JObject jsonstat2AsJObject; + + [TestInitialize] + public void TestSetup() + { + var helper = new JsonStat2Helper(); + CultureInfo ci = new CultureInfo("sv-SE"); + System.Threading.Thread.CurrentThread.CurrentCulture = ci; + System.Threading.Thread.CurrentThread.CurrentUICulture = ci; + + PXModel myModel = helper.GetSelectAllModel("tab_12909_metaid.px"); + + var actual = helper.GetActual(myModel); + + jsonstat2AsJObject = JObject.Parse(actual); + } + + [TestMethod] + [Description("Testing output links from metaid")] + public void TestTableHomepageLink() + { + string expected_relation = "statistics-homepage"; + //string expected_catagory = null; + string expected_metaid = "STATISTICS:sykefratot"; + string expected_href = "https://www.ssb.no/sykefratot"; + string expected_label = "Statistikkens hjemmeside"; + string expected_type = "text/html"; + + var links = jsonstat2AsJObject["link"]["related"]; + Assert.IsGreaterThan(1, links.Count(), "link.related has count < 2."); + + var homepage = links.Where(i => (string)i["extension"]?["metaid"].ToString() == expected_metaid).FirstOrDefault(); + Assert.IsNotNull(homepage, "Can find metaid " + expected_metaid); + + + Assert.AreEqual(expected_relation, homepage["extension"]?["relation"].ToString()); + + Assert.AreEqual(expected_href, homepage["href"].ToString()); + Assert.AreEqual(expected_label, homepage["label"].ToString()); + Assert.AreEqual(expected_type, homepage["type"].ToString()); + } + + + [TestMethod] + [Description("Testing output links from metaid")] + public void TestDimLink() + { + string expected_relation = "definitions"; + //string expected_catagory = null; + string expected_metaid = "urn:ssb:classification:klass:7"; + string expected_href = "https://www.ssb.no/klass/klassifikasjoner/7"; + string expected_label = "Klassifikasjon for yrke."; + string expected_type = "text/html"; + + var links = jsonstat2AsJObject["dimension"]["Yrke"]["link"]["related"]; + Assert.IsGreaterThan(1, links.Count(), "link.related has count < 2."); + + var aLink = links.Where(i => (string)i["extension"]?["metaid"].ToString() == expected_metaid).FirstOrDefault(); + Assert.IsNotNull(aLink, "Can find metaid " + expected_metaid); + + //var lala = aLink.ToString(); + + + Assert.AreEqual(expected_relation, aLink["extension"]?["relation"].ToString()); + + Assert.AreEqual(expected_href, aLink["href"].ToString()); + Assert.AreEqual(expected_label, aLink["label"].ToString()); + Assert.AreEqual(expected_type, aLink["type"].ToString()); + } + + + + } +} diff --git a/UnitTests/TestFiles/tab_12909_metaid.px b/UnitTests/TestFiles/tab_12909_metaid.px new file mode 100644 index 0000000..b6ac97c --- /dev/null +++ b/UnitTests/TestFiles/tab_12909_metaid.px @@ -0,0 +1,77 @@ +CHARSET="ANSI"; +AXIS-VERSION="2010"; +CODEPAGE="iso-8859-1"; +LANGUAGE="no"; +CREATION-DATE="20260316 15:09"; +DECIMALS=1; +SHOWDECIMALS=1; +MATRIX="12909"; +AGGREGALLOWED=NO; +COPYRIGHT=NO; +SUBJECT-CODE="al"; +SUBJECT-AREA="Arbeid og lønn"; +TITLE="12909: Legemeldt sykefravær (prosent) blant innvandrere (lønnstakere, 16-69 år), etter yrke, landbakgrunn, statistikkvariabel og kvartal"; +CONTENTS="12909: Legemeldt sykefravær (prosent) blant innvandrere (lønnstakere, 16-69 år),"; +STUB="yrke","landbakgrunn"; +HEADING="statistikkvariabel","kvartal"; +CONTVARIABLE="statistikkvariabel"; +VARIABLECODE("yrke")="Yrke"; +VALUES("yrke")="Alle yrker","Ledere"; +VARIABLECODE("landbakgrunn")="LandBakgr"; +VALUES("landbakgrunn")="Alle land","Norden utenom Norge","Nye EU-land etter 2004"; +VARIABLECODE("statistikkvariabel")="ContentsCode"; +VALUES("statistikkvariabel")="Sykefraværsprosent (legemeldt)","Sykefraværsprosent (legemeldt), prosentvis endring fra året før"; +VARIABLECODE("kvartal")="Tid"; +VALUES("kvartal")="2025K4"; +TIMEVAL("kvartal")=TLIST(Q1),"2025K4"; +CODES("yrke")="0-9","1"; +CODES("landbakgrunn")="999","00","015a"; +CODES("statistikkvariabel")="Sykefraversprosent","SykefraversprosEndr"; +CODES("kvartal")="2025K4"; +PRESTEXT("yrke")=2; +PRESTEXT("kvartal")=0; +ELIMINATION("yrke")="Alle yrker"; +ELIMINATION("landbakgrunn")="Alle land"; +UNITS="prosent"; +LAST-UPDATED("Sykefraværsprosent (legemeldt)")="20260226 08:00"; +STOCKFA("Sykefraværsprosent (legemeldt)")="A"; +DAYADJ("Sykefraværsprosent (legemeldt)")=NO; +SEASADJ("Sykefraværsprosent (legemeldt)")=NO; +REFPERIOD("Sykefraværsprosent (legemeldt)")="Kvartal"; +UNITS("Sykefraværsprosent (legemeldt)")="prosent"; +CONTACT("Sykefraværsprosent (legemeldt)")="Unn H. Høydahl, Statistisk sentralbyrå# +47 40 90 23 77#uhh@ssb.no## Arbeidsmarked, Statistisk sentralbyrå# +47 62 88 50 00#arbeidsmarked@ssb.no##Stine Bakke, Statistisk sentralbyrå# +47 91 52 62 51#eba@ssb.no##"; +LAST-UPDATED("Sykefraværsprosent (legemeldt), prosentvis endring fra året før")="20260226 08:00"; +STOCKFA("Sykefraværsprosent (legemeldt), prosentvis endring fra året før")="A"; +DAYADJ("Sykefraværsprosent (legemeldt), prosentvis endring fra året før")=NO; +SEASADJ("Sykefraværsprosent (legemeldt), prosentvis endring fra året før")=NO; +REFPERIOD("Sykefraværsprosent (legemeldt), prosentvis endring fra året før")="Kvartal"; +UNITS("Sykefraværsprosent (legemeldt), prosentvis endring fra året før")="prosent"; +CONTACT("Sykefraværsprosent (legemeldt), prosentvis endring fra året før")="Unn H. Høydahl, Statistisk sentralbyrå# +47 40 90 23 77#uhh@ssb.no## Arbeidsmarked, Statistisk sentralbyrå# +47 62 88 50 00#arbeidsmarked@ssb.no##Stine Bakke, Statistisk sentralbyrå# +47 91 52 62 51#eba@ssb.no##"; +DATABASE="Ekstern PROD database O_STATMETA_24 som 2.4"; +SOURCE="Statistisk sentralbyrå"; +INFOFILE="None"; +NOTE="Store endringer i sykefraværsprosenten fra et år til et annet bør tolkes med forsiktighet. Årsaken kan være at det er få personer i det aktuelle yrket, slik at selv små endringer i sykefraværet kan gi store utslag i statistikken.##Tabellen inkluderer ikk" +"e statsløse lønnstakere eller lønnstakere med ukjent landbakgrunn."; +META-ID="STATISTICS:sykefratot"; +META-ID("yrke")="urn:ssb:classification:klass:7,urn:ssb:conceptvariable:vardok:1118"; +META-ID("landbakgrunn")="urn:ssb:classification:klass:545"; +META-ID("statistikkvariabel","Sykefraværsprosent (legemeldt)")="urn:ssb:conceptvariable:vardok:2236"; +META-ID("statistikkvariabel","Sykefraværsprosent (legemeldt), prosentvis endring fra året før")="urn:ssb:conceptvariable:vardok:2236"; +DATASYMBOL1=".."; +DATASYMBOL2="..."; +DATASYMBOL3=":"; +DATASYMBOLSUM="."; +DATASYMBOLNIL="-"; +DATANOTESUM="."; +TABLEID="12909"; +VARIABLE-TYPE("yrke")="V"; +VARIABLE-TYPE("landbakgrunn")="V"; +VARIABLE-TYPE("kvartal")="T"; +DATA= +6.4 1.2 +5.5 -4.2 +7.3 5.0 +4.9 0.1 +3.8 -8.9 +6.0 6.0 +; \ No newline at end of file From b074462bc632756051377e970b55e3699fa01671 Mon Sep 17 00:00:00 2001 From: JohannesFinsveen Date: Wed, 15 Apr 2026 13:09:59 +0200 Subject: [PATCH 2/4] Added statics to method signatures --- PCAxis.Serializers/JsonStat2Serializer.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/PCAxis.Serializers/JsonStat2Serializer.cs b/PCAxis.Serializers/JsonStat2Serializer.cs index e707ffe..ad86e5a 100644 --- a/PCAxis.Serializers/JsonStat2Serializer.cs +++ b/PCAxis.Serializers/JsonStat2Serializer.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -using System.Data; using System.Globalization; using System.IO; using System.Linq; -using System.Reflection.Emit; using System.Text; using Newtonsoft.Json; @@ -180,9 +178,10 @@ public string BuildJsonStructure(PXModel model) return result; } - private void AddMetaidOnVariable(DimensionValue dimensionValue, Variable variable, string language) + private static void AddMetaidOnVariable(DimensionValue dimensionValue, Variable variable, string language) { - if (String.IsNullOrEmpty(variable.MetaId)){ + if (String.IsNullOrEmpty(variable.MetaId)) + { return; } @@ -192,7 +191,7 @@ private void AddMetaidOnVariable(DimensionValue dimensionValue, Variable variabl } } - private void AddMetaidOnValue(DimensionValue dimensionValue, Variable variable, Value variableValue, string language) + private static void AddMetaidOnValue(DimensionValue dimensionValue, Variable variable, Value variableValue, string language) { if (String.IsNullOrEmpty(variableValue.MetaId)) { @@ -201,13 +200,13 @@ private void AddMetaidOnValue(DimensionValue dimensionValue, Variable variable, // There are 3 props on a "Variable Value": Code + Value + Text (combo of Code and Value) // - foreach (var metalink in MetaIdResolverStatic.GetValueLinks(variableValue.MetaId,language,variable.Name, variableValue.Text)) + foreach (var metalink in MetaIdResolverStatic.GetValueLinks(variableValue.MetaId, language, variable.Name, variableValue.Text)) { JsonStat2Dataset.AddRelatedLink(dimensionValue, ToRelatedLink(metalink, variableValue.Code)); } } - private void AddMetaid(JsonStat2Dataset dataset, string metaIdRaw, string language) + private static void AddMetaid(JsonStat2Dataset dataset, string metaIdRaw, string language) { if (String.IsNullOrEmpty(metaIdRaw)) { @@ -218,7 +217,7 @@ private void AddMetaid(JsonStat2Dataset dataset, string metaIdRaw, string langu { dataset.AddRelatedLink(ToRelatedLink(metalink, null)); } - + } @@ -227,7 +226,7 @@ private static RelatedLink ToRelatedLink(Util.MetaId.Link metalink, string categ RelatedLink myOut = new RelatedLink(); myOut.Extension = new RelatedLinkExtension(); myOut.Extension.Relation = metalink.Relation; - if (! String.IsNullOrEmpty(category )) + if (!String.IsNullOrEmpty(category)) { myOut.Extension.Category = category; } From 8c6aac12e2a3f89f34b11ac6ad386b39266ab0c8 Mon Sep 17 00:00:00 2001 From: JohannesFinsveen Date: Fri, 24 Apr 2026 14:26:57 +0200 Subject: [PATCH 3/4] Refactor: moved methods to align with metadata serializer in pxwebapi --- .../JsonStat2/Model/JsonStat2Dataset.cs | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs b/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs index 5956ed4..0523ec1 100644 --- a/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs +++ b/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs @@ -320,42 +320,7 @@ public static void AddDimensionLink(DimensionValue dimensionValue, Dictionary(); - - dimensionValue.Extension.MeasuringType.Add(valueCode, measuringType); - } - - public static void AddPriceType(DimensionValue dimensionValue, string valueCode, PriceType priceType) - { - if (dimensionValue.Extension.PriceType == null) - dimensionValue.Extension.PriceType = new Dictionary(); - - dimensionValue.Extension.PriceType.Add(valueCode, priceType); - } - - public static void AddAdjustment(DimensionValue dimensionValue, string valueCode, Adjustment adjustment) - { - if (dimensionValue.Extension.Adjustment == null) - dimensionValue.Extension.Adjustment = new Dictionary(); - - dimensionValue.Extension.Adjustment.Add(valueCode, adjustment); - } - - public static void AddBasePeriod(DimensionValue dimensionValue, string valueCode, string basePeriod) - { - if (!string.IsNullOrEmpty(basePeriod)) - { - if (dimensionValue.Extension.BasePeriod == null) - dimensionValue.Extension.BasePeriod = new Dictionary(); - - dimensionValue.Extension.BasePeriod.Add(valueCode, basePeriod); - } - } - - //On Dimension + //On Dimension public static void AddRelatedLink(DimensionValue dimensionValue, RelatedLink theLink) { InitializeRelated(dimensionValue); @@ -394,5 +359,42 @@ private void InitiallizeRelated() this.Link.Related = new List(); } } + + public static void AddMeasuringType(DimensionValue dimensionValue, string valueCode, MeasuringType measuringType) + { + if (dimensionValue.Extension.MeasuringType == null) + dimensionValue.Extension.MeasuringType = new Dictionary(); + + dimensionValue.Extension.MeasuringType.Add(valueCode, measuringType); + } + + public static void AddPriceType(DimensionValue dimensionValue, string valueCode, PriceType priceType) + { + if (dimensionValue.Extension.PriceType == null) + dimensionValue.Extension.PriceType = new Dictionary(); + + dimensionValue.Extension.PriceType.Add(valueCode, priceType); + } + + public static void AddAdjustment(DimensionValue dimensionValue, string valueCode, Adjustment adjustment) + { + if (dimensionValue.Extension.Adjustment == null) + dimensionValue.Extension.Adjustment = new Dictionary(); + + dimensionValue.Extension.Adjustment.Add(valueCode, adjustment); + } + + public static void AddBasePeriod(DimensionValue dimensionValue, string valueCode, string basePeriod) + { + if (!string.IsNullOrEmpty(basePeriod)) + { + if (dimensionValue.Extension.BasePeriod == null) + dimensionValue.Extension.BasePeriod = new Dictionary(); + + dimensionValue.Extension.BasePeriod.Add(valueCode, basePeriod); + } + } + + } } From 6e31f88d4b9d7d7bad42833815ef5ae25cf4822d Mon Sep 17 00:00:00 2001 From: JohannesFinsveen Date: Fri, 24 Apr 2026 14:45:25 +0200 Subject: [PATCH 4/4] Removed 2 newlines --- PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs b/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs index 0523ec1..22fefba 100644 --- a/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs +++ b/PCAxis.Serializers/JsonStat2/Model/JsonStat2Dataset.cs @@ -320,7 +320,7 @@ public static void AddDimensionLink(DimensionValue dimensionValue, Dictionary