From 911c34726b131d06544dc0df4c13d39f456f76f5 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 17 Feb 2025 14:54:26 +0100 Subject: [PATCH] The norwegian system --- .../PCAxis.Metadata.UnitTest.csproj | 14 + PCAxis.Metadata.UnitTest/UnitTest1.cs | 53 +- PCAxis.Metadata.UnitTest/metadata.config | 34 ++ PCAxis.Metadata/MetaLinkManagerNorway_cs.txt | 503 ++++++++++++++++++ 4 files changed, 596 insertions(+), 8 deletions(-) create mode 100644 PCAxis.Metadata.UnitTest/metadata.config create mode 100644 PCAxis.Metadata/MetaLinkManagerNorway_cs.txt diff --git a/PCAxis.Metadata.UnitTest/PCAxis.Metadata.UnitTest.csproj b/PCAxis.Metadata.UnitTest/PCAxis.Metadata.UnitTest.csproj index 89ef575..04d4ef0 100644 --- a/PCAxis.Metadata.UnitTest/PCAxis.Metadata.UnitTest.csproj +++ b/PCAxis.Metadata.UnitTest/PCAxis.Metadata.UnitTest.csproj @@ -6,6 +6,16 @@ false + + + + + + + Always + + + @@ -16,4 +26,8 @@ + + + + diff --git a/PCAxis.Metadata.UnitTest/UnitTest1.cs b/PCAxis.Metadata.UnitTest/UnitTest1.cs index 9e08618..e24a044 100644 --- a/PCAxis.Metadata.UnitTest/UnitTest1.cs +++ b/PCAxis.Metadata.UnitTest/UnitTest1.cs @@ -1,13 +1,50 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; namespace PCAxis.Metadata.UnitTest { - [TestClass] - public class UnitTest1 - { - [TestMethod] - public void TestMethod1() - { - } - } + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void TestMethod1() + { + string metaid_raw = "KORTNAVN:aku"; + string expectedLink = "https://www.ssb.no/aku#om-statistikken"; + string expectedLinkText = "Om statistikken"; + + var metaLinkCreator = new MetaLinkManager(); + Assert.IsTrue(metaLinkCreator.LoadConfiguration("metadata.config")); + + + var links = metaLinkCreator.GetTableLinks(metaid_raw, "no"); + Assert.AreEqual(expectedLink, links[0].Link); + Assert.AreEqual(expectedLinkText, links[0].LinkText); + + } + + [TestMethod] + public void TestMethod2() + { + string metaid_raw = "urn:ssb:classification:klass:123"; + //string expectedLink = "https://www.ssb.no/aku#om-statistikken"; + //string expectedLinkText = "Om statistikken"; + + var metaLinkCreator = new MetaLinkManager(); + Assert.IsTrue(metaLinkCreator.LoadConfiguration("metadata.config")); + + + //var links = metaLinkCreator.GetTableLinks(metaid_raw, "no"); + //Assert.AreEqual(expectedLink, links[0].Link); + //Assert.AreEqual(expectedLinkText, links[0].LinkText); + + var varLinks = metaLinkCreator.GetVariableLinks(metaid_raw, "no"); + // returns empty with orginal code. The problem is that "urn:ssb:classification:klass" id the system Id + // , but : is the parameter separator , so things get confusing :-) + Assert.IsTrue(varLinks.Count() > 0, "No link for variable"); + + + } + + } } diff --git a/PCAxis.Metadata.UnitTest/metadata.config b/PCAxis.Metadata.UnitTest/metadata.config new file mode 100644 index 0000000..9477580 --- /dev/null +++ b/PCAxis.Metadata.UnitTest/metadata.config @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PCAxis.Metadata/MetaLinkManagerNorway_cs.txt b/PCAxis.Metadata/MetaLinkManagerNorway_cs.txt new file mode 100644 index 0000000..5a47941 --- /dev/null +++ b/PCAxis.Metadata/MetaLinkManagerNorway_cs.txt @@ -0,0 +1,503 @@ +using PCAxis.Metadata; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Xml; + +namespace Norway.PCAxis.Metadata +{ + /// + /// Class that encapsulates a metadata.config file containing link-definitions to metadata systems + /// + public class MetaLinkManager : IMetaIdProvider + { + /// + /// Class holding format information for a link + /// + private class MetaLinkFormat + { + /// + /// Format of the link text + /// + public string LinkTextFormat { get; set; } + + /// + /// Format of the link (URL) + /// + public string LinkFormat { get; set; } + + /// + /// Hyperlink target + /// Valid values: + /// _blank - Load in new window + /// _self - Load in the same frame as it was clicked + /// _parent - Load in the parent frameset + /// _top - Load in the full body of the window + /// framename - Load in named frame + /// + public string Target { get; set; } + + /// + /// Get number of expected parameters to the text link + /// + /// + public int NumberOfTextParameters() + { + return GetNumberOfParameters(LinkTextFormat); + } + + /// + /// Get number of expected parameters to the link + /// + /// + public int NumberOfLinkParameters() + { + return GetNumberOfParameters(LinkFormat); + } + + /// + /// Internal method for finding the number of expected parameters + /// + /// + /// + private int GetNumberOfParameters(string txt) + { + int count = 0; + int paramIndex = 0; + int index = 0; + bool ok = true; + + if (string.IsNullOrWhiteSpace(txt)) + { + return 0; + } + + + while (ok) + { + index = txt.IndexOf("{" + paramIndex.ToString() + "}", index); // Find {0}, next time {1} and so on... + if (index != -1) + { + count += 1; + paramIndex += 1; + } + else + { + ok = false; + } + } + + return count; + } + } + + + + + + #region "Private fields" + + /// + /// Configuration file + /// + XmlDocument _xdoc; + + /// + /// List of available metadata systems + /// + private List _metadataSystems; + + private HashSet _ignoreMetaidPrefixList = new HashSet(); + + /// + /// Dictionary of metadata systems containing table information. + /// Key = Metadata system id, Value = dictionary of linkformats per language (key = language, value = linkformat-object). + /// + private Dictionary> _tableLinkFormats; + + /// + /// Dictionary of metadata systems containing variable information. + /// Key = Metadata system id, Value = dictionary of linkformats per language (key = language, value = linkformat-object). + /// + private Dictionary> _variableLinkFormats; + + /// + /// Dictionary of metadata systems containing value information. + /// Key = Metadata system id, Value = dictionary of linkformats per language (key = language, value = linkformat-object). + /// + private Dictionary> _valueLinkFormats; + + /// + /// Logging to Log4Net + /// + private static log4net.ILog _logger = log4net.LogManager.GetLogger(typeof(MetaLinkManager)); + + /// + /// Character that separates the systems within a META-ID + /// + private char[] _systemSeparator = { ',' }; + + /// + /// Character that separates the parameters within a system META-ID + /// + private char[] _paramSeparator = { ':' }; + + /// + /// Integer that defines how many parametes a metaIdsystem consist of + /// + private int _numberOfMetaIdParams; + + #endregion + + + + + + /// + /// Constructor + /// + public MetaLinkManager() + { + _metadataSystems = new List(); + _tableLinkFormats = new Dictionary>(); + _variableLinkFormats = new Dictionary>(); + _valueLinkFormats = new Dictionary>(); + } + + /// + /// Returns the param separator + /// + /// + public char[] GetParamSeparator() + { + return _paramSeparator; + } + + /// + /// Returns the system separator + /// + /// + public char[] GetSystemSeparator() + { + return _systemSeparator; + } + + /// + /// Load metadata.config file + /// + /// Path to the configuration file + /// True if the configuration file was successfully loaded, else false + public bool LoadConfiguration(string configurationFile) + { + if (!System.IO.File.Exists(configurationFile)) + { + _logger.ErrorFormat("Metadata configuration file '{0}' does not exist", configurationFile); + return false; + } + + _xdoc = new XmlDocument(); + _xdoc.Load(configurationFile); + + // Table-level + LoadConfigurationSection("onTable", _tableLinkFormats); + + // Variable-level + LoadConfigurationSection("onVariable", _variableLinkFormats); + + // Value-level + LoadConfigurationSection("onValue", _valueLinkFormats); + + return true; + } + + /// + /// Load sub section of the configuration file + /// + /// Name of the section + /// Dictionary to store section data in + /// + private bool LoadConfigurationSection(string section, Dictionary> dictionary) + { + string xpath; + XmlNode node; + XmlNodeList xmlnodes; + + xpath = "/metaId/" + section; + node = _xdoc.SelectSingleNode(xpath); + + // Find all system nodes to ignore + xpath = ".//ignoreMetaSystem"; + xmlnodes = node.SelectNodes(xpath); + + foreach (XmlNode ignoreSysNode in xmlnodes) + { + string sysIdIgnore = ignoreSysNode.Attributes["id"].Value; // system id to ignore + + if (!_ignoreMetaidPrefixList.Contains(sysIdIgnore)) + { + _ignoreMetaidPrefixList.Add(sysIdIgnore); + } + } + + // Find all system nodes + xpath = ".//metaSystem"; + xmlnodes = node.SelectNodes(xpath); + + foreach (XmlNode sysNode in xmlnodes) + { + string sysId = sysNode.Attributes["id"].Value; // system id + + if (!string.IsNullOrWhiteSpace(sysId)) + { + AddMetadataSystem(sysId); + + if (!dictionary.ContainsKey(sysId)) + { + dictionary.Add(sysId, new Dictionary()); // add system to dictionary + + // Find all language nodes for the system + xpath = ".//link"; + XmlNodeList langNodes = sysNode.SelectNodes(xpath); + + foreach (XmlNode langNode in langNodes) + { + string language = langNode.Attributes["px-lang"].Value; + string textFormat = langNode.Attributes["labelStringFormat"].Value; + string linkFormat = langNode.Attributes["urlStringFormat"].Value; + string target; + if ((langNode.Attributes["target"] != null) && (!string.IsNullOrEmpty(langNode.Attributes["target"].Value))) + { + target = langNode.Attributes["target"].Value; + } + else + { + target = "_blank"; + } + + if (!string.IsNullOrWhiteSpace(language) && !string.IsNullOrWhiteSpace(textFormat) && !string.IsNullOrWhiteSpace(linkFormat)) + { + if (!dictionary[sysId].ContainsKey(language)) + { + MetaLinkFormat format = new MetaLinkFormat(); + format.LinkTextFormat = textFormat; + format.LinkFormat = linkFormat; + format.Target = target; + + dictionary[sysId].Add(language, format); // Add format for this language to dictionary + } + } + } + + } + } + + } + + return true; + } + + + /// + /// Add metadatasystem to list of available systems + /// + /// Id of the metadatasystem + private void AddMetadataSystem(string system) + { + foreach (MetadataSystem sys in _metadataSystems) + { + if (sys.ID.Equals(system)) + { + return; + } + } + + _metadataSystems.Add(new MetadataSystem(system, system)); + } + + private bool IsMetaIdInIgnoreList(string metaId) + { + return _ignoreMetaidPrefixList.Any(x => metaId.StartsWith(x + ":")); + } + + /// + /// Create links + /// + /// META-ID + /// Language + /// Dictionary containing the link formats + /// + private MetaLink[] GetLinks(string metaIdList, string language, Dictionary> dictionary) + { + List lst = new List(); + + string[] metaIds = metaIdList.Split(_systemSeparator, StringSplitOptions.RemoveEmptyEntries); + + foreach (string metaId in metaIds) + { + string theMetadataSystemId = ""; + + foreach (string aMetadataSystemId in dictionary.Keys) + { + if (metaId.StartsWith(aMetadataSystemId)) + { + theMetadataSystemId = aMetadataSystemId; + break; + } + } + + if (String.IsNullOrEmpty(theMetadataSystemId)) + { + if (!IsMetaIdInIgnoreList(metaId)) + { + _logger.ErrorFormat("Cant find MetadataSystem for {0}", metaId); + } + + continue; + } + + string rawParamsString = metaId.Replace(theMetadataSystemId, ""); + string[] linkParams = rawParamsString.Split(_paramSeparator, StringSplitOptions.RemoveEmptyEntries); + + // + if (dictionary[theMetadataSystemId].ContainsKey(language)) + { + + // Get format object from dictionary + MetaLinkFormat format = dictionary[theMetadataSystemId][language]; + + MetaLink lnk = new MetaLink(); + lnk.System = theMetadataSystemId; + + + try + { + // Create link text + if (theMetadataSystemId == "urn:ssb:classification:klass") + { + lnk.LinkText = GetKlassHeaderFromAPI(linkParams[0], format.Target); + if (string.IsNullOrEmpty(lnk.LinkText)) + { + //lnk.Link = string.Format(format.LinkFormat, linkParams); + lnk.LinkText = string.Format(format.LinkTextFormat, linkParams); + } + lnk.Link = string.Format(format.LinkFormat, linkParams); + } + else if (theMetadataSystemId == "urn:ssb:contextvariable:common") + { + lnk.Link = string.Format(format.LinkFormat, linkParams) + "&lang=" + language; + lnk.LinkText = string.Format(format.LinkTextFormat, linkParams); + } + else + { + lnk.Link = string.Format(format.LinkFormat, linkParams); + lnk.LinkText = string.Format(format.LinkTextFormat, linkParams); + } + + + // Create the link + //lnk.Link = string.Format(format.LinkFormat, linkParams); + lnk.Target = format.Target; + + // Add link to the return-list + lst.Add(lnk); + } + catch (System.FormatException e) + { + _logger.ErrorFormat("Problem for metadataSystem {0}, rawParamsString {1}", theMetadataSystemId, rawParamsString); + _logger.Error("The problem: ", e); + } + + } + } + + + + return lst.ToArray(); + } + + + private int GetNumberOfSystemParameters(string systemId) + { + try + { + return systemId.Split(_paramSeparator[0]).Length; + } + catch + { + return 1; + } + } + private String getSystemMetaId(object[] systemLinkParams, int _numberOfMetaIdParams) + { + string sysId = ""; + if (_numberOfMetaIdParams < systemLinkParams.Length) + { + for (int i = 0; i < _numberOfMetaIdParams; i++) + { + sysId += (string)systemLinkParams[i] + ":"; + } + sysId = sysId.Remove(sysId.LastIndexOf(":")); + } + return sysId; + } + + + #region "Implementation of IMetaIdProvider" + + public bool Initialize(string configurationFile) + { + return LoadConfiguration(configurationFile); + } + + public MetadataSystem[] MetadataSystems + { + get + { + return _metadataSystems.ToArray(); + } + } + + public MetaLink[] GetTableLinks(string metaId, string language) + { + return GetLinks(metaId, language, _tableLinkFormats); + } + + public MetaLink[] GetVariableLinks(string metaId, string language) + { + return GetLinks(metaId, language, _variableLinkFormats); + } + + public MetaLink[] GetValueLinks(string metaId, string language) + { + return GetLinks(metaId, language, _valueLinkFormats); + } + + public String GetKlassHeaderFromAPI(string id, string target) + { + try + { + var klassUrl = string.Format(target, id); + WebRequest objRequest = HttpWebRequest.Create(klassUrl); + objRequest.Timeout = 3000; //No time for config now.... + + using (WebResponse objResponse = objRequest.GetResponse()) + { + using (var sr = new System.IO.StreamReader(objResponse.GetResponseStream(), System.Text.Encoding.UTF8)) + { + var rawString = sr.ReadToEnd(); + var json = JObject.Parse(rawString); + return (string)json["name"]; + } + } + } + catch + { + return null; + } + } + + + #endregion + } +}