From 65a8df2715aa300ce0a77664398b85b7f157e6ea Mon Sep 17 00:00:00 2001 From: "Yang, Le" Date: Thu, 13 Jul 2023 09:22:33 -0400 Subject: [PATCH 1/4] fix: show record errors when testing messages --- .../src/components/misc/info/Property.js | 2 +- .../src/components/tests/Connectathon.js | 15 ++- .../tests/MessageConnectathonProducing.js | 15 ++- canary/Models/Message.cs | 2 +- canary/Models/Test.cs | 121 ++++++++++++------ 5 files changed, 109 insertions(+), 46 deletions(-) diff --git a/canary/ClientApp/src/components/misc/info/Property.js b/canary/ClientApp/src/components/misc/info/Property.js index d9be954..0177d9a 100644 --- a/canary/ClientApp/src/components/misc/info/Property.js +++ b/canary/ClientApp/src/components/misc/info/Property.js @@ -86,7 +86,7 @@ export class Property extends Component { error={error} /> ); - } else if (type === 'StringArr') { + } else if (type === 'StringArr' || type === 'List`1') { return ( +
diff --git a/canary/ClientApp/src/components/tests/MessageConnectathonProducing.js b/canary/ClientApp/src/components/tests/MessageConnectathonProducing.js index 23b0e53..ab10000 100644 --- a/canary/ClientApp/src/components/tests/MessageConnectathonProducing.js +++ b/canary/ClientApp/src/components/tests/MessageConnectathonProducing.js @@ -132,10 +132,18 @@ export class MessageConnectathonProducing extends Component { }); } - downloadAsFile(contents) { + downloadAsFile(contents, type = 'html') { + var ext = 'html'; + var encoding = 'text/html;charset=utf-8'; + + if (type === 'json') { + ext = 'json'; + encoding = 'application/json;charset=utf-8'; + } + var element = document.createElement('a'); - element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(contents)); - element.setAttribute('download', `canary-report-${this.connectathonRecordName(this.props.params.id).toLowerCase()}-${new Date().getTime()}.html`); + element.setAttribute('href', `data:${encoding},` + encodeURIComponent(contents)); + element.setAttribute('download', `canary-report-${this.connectathonRecordName(this.props.params.id).toLowerCase()}-${new Date().getTime()}.${ext}`); element.click(); } @@ -203,6 +211,7 @@ export class MessageConnectathonProducing extends Component { +
diff --git a/canary/Models/Message.cs b/canary/Models/Message.cs index c2da720..dfd4492 100644 --- a/canary/Models/Message.cs +++ b/canary/Models/Message.cs @@ -19,7 +19,7 @@ public class Message { "MessageId", "The Message Identifier" }, { "MessageType", "The NCHS Message Type" }, { "MessageSource", "The Jurisdiction Message Source" }, - { "MessageDestination", "The NCHS Message Endpoint" }, + { "MessageDestinations", "The NCHS Message Endpoints" }, { "CertificateNumber", "Death Certificate Number (DeathRecord Identifier)" }, { "StateAuxiliaryIdentifier", "Auxiliary State File Number (DeathRecord BundleIdentifier)" }, { "NCHSIdentifier", "The NCHS compound identifier for the supplied DeathRecord" }, diff --git a/canary/Models/Test.cs b/canary/Models/Test.cs index 7b80af2..cfbc1e8 100644 --- a/canary/Models/Test.cs +++ b/canary/Models/Test.cs @@ -6,6 +6,7 @@ using Hl7.Fhir.Serialization; using Hl7.FhirPath; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using VRDR; namespace canary.Models @@ -59,13 +60,15 @@ public Test(DeathRecord record) public Test Run(string description) { - if(Type.Contains("Message")) { + if (Type.Contains("Message")) + { TestMessage = new Message(description); - Results = MessageCompare(); + Results = JsonConvert.SerializeObject(MessageCompare()); } - else { + else + { TestRecord = new Record(DeathRecord.FromDescription(description)); - Results = RecordCompare(); + Results = JsonConvert.SerializeObject(RecordCompare()); } CompletedDateTime = DateTime.Now; CompletedBool = true; @@ -75,24 +78,25 @@ public Test Run(string description) public Test Run(DeathRecord record) { TestRecord = new Record(record); - RecordCompare(); + Results = JsonConvert.SerializeObject(RecordCompare()); CompletedDateTime = DateTime.Now; CompletedBool = true; return this; } - public string MessageCompare() + public Dictionary> MessageCompare() { Dictionary> description = new Dictionary>(); BaseMessage bundle = TestMessage.GetMessage(); DeathRecord record = ReferenceRecord.GetRecord(); BaseMessage referenceBundle = new Message(ReferenceRecord, bundle.MessageType).GetMessage(); + // // On the frontend this shares the same view as the RecordCompare below. This heading // is shown above the results in the app. string heading = "Message Validation Results"; description.Add(heading, new Dictionary()); Dictionary category = description[heading]; - foreach(PropertyInfo property in bundle.GetType().GetProperties()) + foreach (PropertyInfo property in bundle.GetType().GetProperties()) { // Add the new property to the category category[property.Name] = new Dictionary(); @@ -109,10 +113,17 @@ public string MessageCompare() DeathRecord extracted = (DeathRecord)property.GetValue(bundle); TestRecord = new Record(extracted); int previousIncorrect = Incorrect; - category[property.Name]["SnippetJSON"] = RecordCompare(); - // See if the value of Incorrect changed in 'RecordCompare' and use that to determine if the - // Record matches or not. - category[property.Name]["Match"] = previousIncorrect.Equals(Incorrect) ? "true" : "false"; + // Add the record comparison results to the message comparison results + Dictionary> recordCompare = RecordCompare(); + foreach (KeyValuePair> entry in recordCompare) + { + description.Add(entry.Key, entry.Value); + } + category.Remove(property.Name); + //category[property.Name]["SnippetJSON"] = JsonConvert.SerializeObject(recordCompare); + // // See if the value of Incorrect changed in 'RecordCompare' and use that to determine if the + // // Record matches or not. + // category[property.Name]["Match"] = previousIncorrect.Equals(Incorrect) ? "true" : "false"; } else if (Message.validatePresenceOnly(property.Name)) { @@ -129,7 +140,24 @@ public string MessageCompare() // Using == here seems to be checking ReferenceEquals and not Equals, causing the equality to return false. // Calling Prop1.Equals(Prop2) here raises an error if the value is null in the ReferenceBundle. // The best option here is to just use the object.Equals operator. - if (Equals(property.GetValue(referenceBundle), property.GetValue(bundle))) + // JToken.DeepEquals(property.GetValue(referenceBundle), property.GetValue(bundle))) + + // if (Equals(property.GetValue(referenceBundle), property.GetValue(bundle))) + + var a = property.GetValue(referenceBundle); + var b = property.GetValue(bundle); + + if (a == null && b == null) + { + MarkCorrect(); + category[property.Name]["Match"] = "true"; + } + else if (a == null || b == null) + { + MarkIncorrect(); + category[property.Name]["Match"] = "false"; + } + else if (JToken.DeepEquals(JToken.FromObject(a), JToken.FromObject(b))) { MarkCorrect(); category[property.Name]["Match"] = "true"; @@ -141,13 +169,14 @@ public string MessageCompare() } } } - return JsonConvert.SerializeObject(description); + + return description; } - public string RecordCompare() + public Dictionary> RecordCompare() { Dictionary> description = new Dictionary>(); - foreach(PropertyInfo property in typeof(DeathRecord).GetProperties().OrderBy(p => p.GetCustomAttribute().Priority)) + foreach (PropertyInfo property in typeof(DeathRecord).GetProperties().OrderBy(p => p.GetCustomAttribute().Priority)) { // Grab property annotation for this property Property info = property.GetCustomAttribute(); @@ -189,7 +218,7 @@ public string RecordCompare() // Make sure to grab all of the Conditions for COD string xml = ""; string json = ""; - foreach(var match in matches) + foreach (var match in matches) { xml += match.ToXml(); json += match.ToJson() + ","; @@ -212,9 +241,9 @@ public string RecordCompare() { category[property.Name]["SnippetXML"] = ""; } - Dictionary jsonRoot = - JsonConvert.DeserializeObject>(matches.First().ToJson(), - new JsonSerializerSettings() { DateParseHandling = DateParseHandling.None }); + Dictionary jsonRoot = + JsonConvert.DeserializeObject>(matches.First().ToJson(), + new JsonSerializerSettings() { DateParseHandling = DateParseHandling.None }); if (jsonRoot != null && jsonRoot.Keys.Contains(path.Element)) { category[property.Name]["SnippetJSON"] = "{" + $"\"{path.Element}\": \"{jsonRoot[path.Element]}\"" + "}"; @@ -247,7 +276,7 @@ public string RecordCompare() // Make sure to grab all of the Conditions for COD string xml = ""; string json = ""; - foreach(var match in matchesTest) + foreach (var match in matchesTest) { xml += match.ToXml(); json += match.ToJson() + ","; @@ -270,9 +299,9 @@ public string RecordCompare() { category[property.Name]["SnippetXMLTest"] = ""; } - Dictionary jsonRoot = - JsonConvert.DeserializeObject>(matchesTest.First().ToJson(), - new JsonSerializerSettings() { DateParseHandling = DateParseHandling.None }); + Dictionary jsonRoot = + JsonConvert.DeserializeObject>(matchesTest.First().ToJson(), + new JsonSerializerSettings() { DateParseHandling = DateParseHandling.None }); if (jsonRoot != null && jsonRoot.Keys.Contains(pathTest.Element)) { category[property.Name]["SnippetJSONTest"] = "{" + $"\"{pathTest.Element}\": \"{jsonRoot[pathTest.Element]}\"" + "}"; @@ -308,7 +337,7 @@ public string RecordCompare() // If this is a coded value, the comparison should be case sensitive StringComparison caseSensitivity = StringComparison.OrdinalIgnoreCase; - if(valueReference.ContainsKey("code")) + if (valueReference.ContainsKey("code")) { caseSensitivity = StringComparison.Ordinal; } @@ -337,29 +366,40 @@ public string RecordCompare() } // Check for match if ((valueReference.ContainsKey(parameter.Key) && valueTest.ContainsKey(parameter.Key)) && - (String.Equals((string)valueReference[parameter.Key], (string)valueTest[parameter.Key], caseSensitivity))) { + (String.Equals((string)valueReference[parameter.Key], (string)valueTest[parameter.Key], caseSensitivity))) + { // Equal MarkCorrect(); moreInfo[parameter.Key]["Match"] = "true"; - } else if ((valueReference.ContainsKey(parameter.Key) && valueTest.ContainsKey(parameter.Key)) && + } + else if ((valueReference.ContainsKey(parameter.Key) && valueTest.ContainsKey(parameter.Key)) && String.IsNullOrWhiteSpace((string)valueReference[parameter.Key]) && - String.IsNullOrWhiteSpace((string)valueTest[parameter.Key])) { + String.IsNullOrWhiteSpace((string)valueTest[parameter.Key])) + { // Equal MarkCorrect(); moreInfo[parameter.Key]["Match"] = "true"; - } else if (!valueReference.ContainsKey(parameter.Key) && !valueTest.ContainsKey(parameter.Key)) { + } + else if (!valueReference.ContainsKey(parameter.Key) && !valueTest.ContainsKey(parameter.Key)) + { // Both null, equal MarkCorrect(); moreInfo[parameter.Key]["Match"] = "true"; - } else if (!valueReference.ContainsKey(parameter.Key) || (valueReference.ContainsKey(parameter.Key) && String.IsNullOrWhiteSpace(valueReference[parameter.Key]))) { + } + else if (!valueReference.ContainsKey(parameter.Key) || (valueReference.ContainsKey(parameter.Key) && String.IsNullOrWhiteSpace(valueReference[parameter.Key]))) + { // Source is empty, so no need to punish test MarkCorrect(); moreInfo[parameter.Key]["Match"] = "true"; - } else if (parameter.Key == "display" && (!valueTest.ContainsKey(parameter.Key) || String.IsNullOrWhiteSpace(valueTest[parameter.Key]))) { + } + else if (parameter.Key == "display" && (!valueTest.ContainsKey(parameter.Key) || String.IsNullOrWhiteSpace(valueTest[parameter.Key]))) + { // Test record had nothing for display, equal MarkCorrect(); moreInfo[parameter.Key]["Match"] = "true"; - } else { + } + else + { // Not equal MarkIncorrect(); moreInfo[parameter.Key]["Match"] = "false"; @@ -382,13 +422,15 @@ public string RecordCompare() MarkCorrect(); category[property.Name]["Match"] = "true"; } - else if (String.IsNullOrWhiteSpace((string)property.GetValue(ReferenceRecord.GetRecord()))) { + else if (String.IsNullOrWhiteSpace((string)property.GetValue(ReferenceRecord.GetRecord()))) + { MarkCorrect(); category[property.Name]["Match"] = "true"; } else if (!String.IsNullOrWhiteSpace((string)property.GetValue(TestRecord.GetRecord())) && !String.IsNullOrWhiteSpace((string)property.GetValue(ReferenceRecord.GetRecord())) && - ((string)property.GetValue(TestRecord.GetRecord())).ToLower().Contains(((string)property.GetValue(ReferenceRecord.GetRecord())).ToLower())) { + ((string)property.GetValue(TestRecord.GetRecord())).ToLower().Contains(((string)property.GetValue(ReferenceRecord.GetRecord())).ToLower())) + { MarkCorrect(); category[property.Name]["Match"] = "true"; } @@ -407,7 +449,8 @@ public string RecordCompare() MarkCorrect(); category[property.Name]["Match"] = "true"; } - else if (String.IsNullOrWhiteSpace((string)property.GetValue(ReferenceRecord.GetRecord()))) { + else if (String.IsNullOrWhiteSpace((string)property.GetValue(ReferenceRecord.GetRecord()))) + { MarkCorrect(); category[property.Name]["Match"] = "true"; } @@ -462,7 +505,7 @@ public string RecordCompare() category[property.Name]["Match"] = "true"; } else - { + { MarkIncorrect(); category[property.Name]["Match"] = "false"; } @@ -486,7 +529,8 @@ public string RecordCompare() MarkCorrect(); category[property.Name]["Match"] = "true"; } - else if (property.GetValue(ReferenceRecord.GetRecord()) == null) { + else if (property.GetValue(ReferenceRecord.GetRecord()) == null) + { MarkCorrect(); category[property.Name]["Match"] = "true"; } @@ -583,7 +627,8 @@ public string RecordCompare() MarkCorrect(); category[property.Name]["Match"] = "true"; } - else if (property.GetValue(ReferenceRecord.GetRecord()) == null) { + else if (property.GetValue(ReferenceRecord.GetRecord()) == null) + { MarkCorrect(); category[property.Name]["Match"] = "true"; } @@ -595,7 +640,7 @@ public string RecordCompare() } } } - return JsonConvert.SerializeObject(description); + return description; } private void MarkCorrect() From 4aa39d72730366eaf7f65ebe60279d26d2292f63 Mon Sep 17 00:00:00 2001 From: "Yang, Le" Date: Thu, 20 Jul 2023 09:31:27 -0400 Subject: [PATCH 2/4] refactor DownloadAsFile as separate component --- .../src/components/misc/DownloadAsFile.js | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 canary/ClientApp/src/components/misc/DownloadAsFile.js diff --git a/canary/ClientApp/src/components/misc/DownloadAsFile.js b/canary/ClientApp/src/components/misc/DownloadAsFile.js new file mode 100644 index 0000000..cff9f7f --- /dev/null +++ b/canary/ClientApp/src/components/misc/DownloadAsFile.js @@ -0,0 +1,35 @@ +import React, { Component } from 'react'; +import { Button, Icon } from 'semantic-ui-react'; +import report from '../report'; + +export class DownloadAsFile extends Component { + displayName = DownloadAsFile.name; + + constructor(props) { + super(props); + this.state = { ...this.props }; + } + + downloadAsFile(contents, type = 'html') { + var ext = 'html'; + var encoding = 'text/html;charset=utf-8'; + + if (type === 'json') { + ext = 'json'; + encoding = 'application/json;charset=utf-8'; + } + + var element = document.createElement('a'); + element.setAttribute('href', `data:${encoding},` + encodeURIComponent(contents)); + element.setAttribute('download', `canary-report-${this.connectathonRecordName(this.props.params.id).toLowerCase()}-${new Date().getTime()}.${ext}`); + element.click(); + } + + render() { + return ( + + + + ); + } +} From 0bc3423c70f8890fa5cf5668bef049a5d71d67a7 Mon Sep 17 00:00:00 2001 From: "Yang, Le" Date: Thu, 20 Jul 2023 12:34:02 -0400 Subject: [PATCH 3/4] revert refactoring DownloadAsFile component Need additional thought on how the refactor should work; downloadAsFile function is used by multiple components including the text editor --- .../src/components/misc/DownloadAsFile.js | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 canary/ClientApp/src/components/misc/DownloadAsFile.js diff --git a/canary/ClientApp/src/components/misc/DownloadAsFile.js b/canary/ClientApp/src/components/misc/DownloadAsFile.js deleted file mode 100644 index cff9f7f..0000000 --- a/canary/ClientApp/src/components/misc/DownloadAsFile.js +++ /dev/null @@ -1,35 +0,0 @@ -import React, { Component } from 'react'; -import { Button, Icon } from 'semantic-ui-react'; -import report from '../report'; - -export class DownloadAsFile extends Component { - displayName = DownloadAsFile.name; - - constructor(props) { - super(props); - this.state = { ...this.props }; - } - - downloadAsFile(contents, type = 'html') { - var ext = 'html'; - var encoding = 'text/html;charset=utf-8'; - - if (type === 'json') { - ext = 'json'; - encoding = 'application/json;charset=utf-8'; - } - - var element = document.createElement('a'); - element.setAttribute('href', `data:${encoding},` + encodeURIComponent(contents)); - element.setAttribute('download', `canary-report-${this.connectathonRecordName(this.props.params.id).toLowerCase()}-${new Date().getTime()}.${ext}`); - element.click(); - } - - render() { - return ( - - - - ); - } -} From 7fb7f20b5aeb5f37c08fb8702bd34e5eb57413fc Mon Sep 17 00:00:00 2001 From: "Yang, Le" Date: Thu, 20 Jul 2023 12:35:51 -0400 Subject: [PATCH 4/4] change button texts --- canary/ClientApp/src/components/tests/Connectathon.js | 2 +- .../src/components/tests/MessageConnectathonProducing.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/canary/ClientApp/src/components/tests/Connectathon.js b/canary/ClientApp/src/components/tests/Connectathon.js index 7016228..3879a5e 100644 --- a/canary/ClientApp/src/components/tests/Connectathon.js +++ b/canary/ClientApp/src/components/tests/Connectathon.js @@ -152,7 +152,7 @@ export class Connectathon extends Component { - +
diff --git a/canary/ClientApp/src/components/tests/MessageConnectathonProducing.js b/canary/ClientApp/src/components/tests/MessageConnectathonProducing.js index ab10000..dc1b5aa 100644 --- a/canary/ClientApp/src/components/tests/MessageConnectathonProducing.js +++ b/canary/ClientApp/src/components/tests/MessageConnectathonProducing.js @@ -211,7 +211,7 @@ export class MessageConnectathonProducing extends Component { - +