Example #1
0
 /// <summary>
 /// Takes a given child element (aChildElement) and parent constraint (aParentTemplateConstraint) and steps through the Children of the parent constraint
 /// to determine if any siblings of the child element exist.
 /// </summary>
 /// <param name="aChildElement">Target element that we need to locate siblings for</param>
 /// <param name="aParentTemplateConstraint">Parent Constraint of the aChildElement</param>
 /// <param name="aParentElement">Parent Element for the aChildElement</param>
 /// <param name="aAddedConstraints">Constraints that have already been added to the child collection, we don't need to parse these.</param>
 static private void AddSiblingElements(DocumentTemplateElement aChildElement, IConstraint aParentTemplateConstraint, DocumentTemplateElement aParentElement, Dictionary <DocumentTemplateElement, IConstraint> aConstraintMap)
 {
     //look at parent to get the siblings
     if (aParentTemplateConstraint.Children != null)
     {
         DocumentTemplateElement          parsedElement   = null;
         DocumentTemplateElementAttribute parsedAttribute = null;
         //walk through the siblings
         foreach (var sibling in aParentTemplateConstraint.Children)
         {
             //have we already added this constraint in a previous iteration (e.g. it's on the main path from the leaf to the root)
             if (sibling.IsBranchIdentifier && !aConstraintMap.ContainsValue(sibling))
             {
                 //parse the context
                 var cp = new ContextParser(sibling.Context);
                 cp.Parse(out parsedElement, out parsedAttribute);
                 //is this an element or an attribute?
                 if ((parsedElement != null) && (!string.IsNullOrEmpty(parsedElement.ElementName)))
                 {
                     parsedElement.IsBranchIdentifier = sibling.IsBranchIdentifier;
                     parsedElement.IsBranch           = sibling.IsBranch;
                     //element, let's add it to the parent element's children so it becomes a sibling of aChildElement
                     parsedElement.Value = sibling.Value;
                     aParentElement.AddElement(parsedElement);
                     AddBranchedAttributes(parsedElement, sibling);
                     aConstraintMap.Add(parsedElement, sibling);
                 }
             }
         }
     }
 }
Example #2
0
        static public DocumentTemplateElement CreateParentElementForAttribute(IConstraint aConstraint, DocumentTemplateElementAttribute aAttribute)
        {
            DocumentTemplateElement parentElement = null;

            // Is there a parent constraint that should be used as a context
            if (aConstraint.Parent != null && !string.IsNullOrEmpty(aConstraint.Parent.Context) && !aConstraint.Parent.IsBranch)
            {
                if ((aAttribute != null) && (aAttribute.Element == null)) //we have an attribute, but no element attached. the parent constraint would then be the element.
                {
                    parentElement = new DocumentTemplateElement(aConstraint.Parent.Context);
                    if (aConstraint.Parent.IsBranch)
                    {
                        parentElement.AddAttribute(aAttribute);
                    }
                }
                else if ((aAttribute != null) && (aAttribute.Element != null) && (aConstraint.Parent.Context != aAttribute.Element.ElementName)) //we have an attribute, with an element attached, but the element does not match the parent context
                {
                    parentElement = new DocumentTemplateElement(aConstraint.Parent.Context);
                    parentElement.AddElement(aAttribute.Element);
                }
                else if (aAttribute != null && aAttribute.Element != null)
                {
                    parentElement = aAttribute.Element;
                }
            }
            return(parentElement);
        }
        /// <summary>
        /// Uses back-tracing algorithm to go backwards through the tree
        /// Helper function which builds the full parent context for a given template constraint. For example, for the template constraint @code with cda:entryRelationship/cda:observation/cda:code[@code]
        /// this function returns the cda:entryRelationship/cda:observation/cda:code.
        /// </summary>
        /// <param name="aElement">current element to start from</param>
        /// <param name="aTemplateConstraint">constraint which will have its parent chain walked to form path</param>
        /// <param name="aIncludeElementInPath">determines whether we start the path with the element passed in (true) or its parent (false)</param>
        /// <returns>full context string</returns>
        public static string CreateFullParentContext(string aPrefix, IConstraint aTemplateConstraint)
        {
            if (aTemplateConstraint == null)
            {
                return(string.Empty);
            }

            DocumentTemplateElement          firstElement    = null;
            DocumentTemplateElement          newElement      = null;
            DocumentTemplateElement          previousElement = null;
            DocumentTemplateElementAttribute newAttribute    = null;
            IConstraint currentConstraint = aTemplateConstraint.Parent;

            while (currentConstraint != null)
            {
                //parse the context to determine whether this is element or attribute
                var contextParser = new ContextParser(currentConstraint.Context);
                contextParser.Parse(out newElement, out newAttribute);
                newElement.Attributes.Clear();
                if (currentConstraint.IsBranch) //if we hit a branch then we stop b/c we are in the branch's context
                {
                    break;
                }
                if (newElement == null)
                {
                    break;  //there is a broken chain, we have null parent
                }
                //add value and data type (if present)
                ConstraintToDocumentElementHelper.AddElementValueAndDataType(aPrefix, newElement, currentConstraint);
                //chain the previous element to the child collection of this new one
                if (previousElement != null)
                {
                    newElement.AddElement(previousElement);
                }
                //get the leaf node
                if (firstElement == null)
                {
                    firstElement = newElement;
                }
                previousElement = newElement;
                //walk the parent chain
                currentConstraint = currentConstraint.Parent;
            }
            if (firstElement == null)
            {
                return(string.Empty);
            }
            else
            {
                var contextBuilder = new ContextBuilder(firstElement, aPrefix);
                return(contextBuilder.GetFullyQualifiedContextString());
            }
        }
        public void TestCDADocumentTemplate_WithValidNamespace_SingleChildElement_SingleChildElement_ChildCountIsOne()
        {
            var cdaDocumentTemplate = new DocumentTemplate("urn:hl7-org:v3");
            var element             = new DocumentTemplateElement("author");

            Assert.IsNotNull(element.ChildElements, "element.ChildElements is null, expected instance");
            element.AddElement(new DocumentTemplateElement("assignedPerson"));
            cdaDocumentTemplate.AddElement(element);
            Assert.IsNotNull(cdaDocumentTemplate.ChildElements, "cdaDocumentTemplate.RootElements is null, expected instance.");
            Assert.IsTrue(cdaDocumentTemplate.ChildElements.Count == 1, "Root element count failed, expected 1, actual {0}", cdaDocumentTemplate.ChildElements.Count);
            Assert.IsTrue(cdaDocumentTemplate.ChildElements[0].ChildElements.Count == 1, "Child element count failed, expected 1, actual {0}", cdaDocumentTemplate.ChildElements[0].ChildElements.Count);
            Assert.AreEqual(cdaDocumentTemplate.ChildElements[0].ChildElements[0].ParentElement, cdaDocumentTemplate.ChildElements[0], "Child element parent was not set properly.");
        }
Example #5
0
        public void GenerateContext_SingleChildElement_4Levels_GenerateContextOn3rdLevel()
        {
            var administrativeCodeElement = new DocumentTemplateElement("administrativeGenderCode");

            administrativeCodeElement.AddAttribute(new DocumentTemplateElementAttribute("code", "20", string.Empty, "MMG-GENDER-CODE-OID"));
            var patientCodeElement  = new DocumentTemplateElement("patient");
            var cdaDocumentTemplate = new DocumentTemplate("urn:hl7-org:v3");

            cdaDocumentTemplate.AddElement(new DocumentTemplateElement("recordTarget")
                                           .AddElement(new DocumentTemplateElement("patientRole")
                                                       .AddElement(patientCodeElement
                                                                   .AddElement(administrativeCodeElement))));
            var contextBuilder = new ContextBuilder(patientCodeElement, "cda");
            var context        = contextBuilder.GetFullyQualifiedContextString();
            var expected       = "cda:recordTarget/cda:patientRole/cda:patient";

            Assert.IsFalse(string.IsNullOrEmpty(context), "Null or empty string returned by context builder");
            Assert.IsTrue(context == expected, "Context string was not correct, expected '{0}', actual '{1}'", expected, context);
        }
Example #6
0
        public void Parse(out DocumentTemplateElement aContextElement, out DocumentTemplateElementAttribute aAttribute)
        {
            aContextElement = null; //default
            aAttribute      = null; //default

            var parsedContext = _context.Split('/');

            if (parsedContext.Length > 1)   //does the context contain a complex element structure (e.g. code/@code)
            {
                DocumentTemplateElement parentContextElement = null;
                for (int i = 0; i < parsedContext.Length; i++)
                {
                    if (IsAttribute(parsedContext[i]))
                    {
                        aAttribute = new DocumentTemplateElementAttribute(parsedContext[i].Replace("@", ""));
                        aContextElement.AddAttribute(aAttribute);
                    }
                    else
                    {
                        aContextElement = new DocumentTemplateElement(parsedContext[i]);
                        if (parentContextElement != null)
                        {
                            parentContextElement.AddElement(aContextElement);
                        }
                    }
                    parentContextElement = aContextElement;
                }
            }
            else
            {
                if (IsAttribute(_context))
                {
                    aAttribute = new DocumentTemplateElementAttribute(_context.Replace("@", ""));
                }
                else
                {
                    aContextElement = new DocumentTemplateElement(_context);
                }
            }
        }
Example #7
0
        public void BuildAdvanceDirectiveObservationDocument_1stLevelOnly()
        {
            var sectionCount = 1;
            var phase        = new Phase();

            phase.ID = "error";
            var document = new SchematronDocument();

            document.Phases.Add(phase);


            var doc = new DocumentTemplate("cda");

            doc.AddElement(new DocumentTemplateElement("observation"));
            doc.ChildElements[0].AddAttribute(new DocumentTemplateElementAttribute("classCode", "OBS"));
            doc.ChildElements[0].AddAttribute(new DocumentTemplateElementAttribute("moodCode", "EVN"));
            doc.AddElement(new DocumentTemplateElement("templateId"));
            doc.ChildElements[1].AddAttribute(new DocumentTemplateElementAttribute("root", "2.16.840.1.113883.10.20.22.4.48"));
            doc.AddElement(new DocumentTemplateElement("id"));
            doc.AddElement(new DocumentTemplateElement("code"));
            doc.ChildElements[doc.ChildElements.Count - 1].AddAttribute(new DocumentTemplateElementAttribute("xsi-type", "CE", "2.16.840.1.113883.1.11.20.2"));
            doc.AddElement(new DocumentTemplateElement("statusCode"));
            doc.ChildElements[doc.ChildElements.Count - 1].AddAttribute(new DocumentTemplateElementAttribute("code", "completed", "2.16.840.1.113883.5.14"));
            var participantElement = new DocumentTemplateElement("participant");

            doc.ChildElements[0].AddElement(participantElement);
            participantElement.AddAttribute(new DocumentTemplateElementAttribute("typeCode", "VRF"));
            var templateIdElement = new DocumentTemplateElement("templateId");

            templateIdElement.AddAttribute(new DocumentTemplateElementAttribute("root", "2.16.840.1.113883.10.20.1.58"));
            participantElement.AddElement(templateIdElement);
            var timeElement = new DocumentTemplateElement("time");

            timeElement.AddAttribute(new DocumentTemplateElementAttribute("xsi:type", "TS"));
            participantElement.AddElement(timeElement);
            var participantRoleElement = new DocumentTemplateElement("participantRole");

            participantElement.AddElement(participantRoleElement);


            var contextBuilder = new ContextBuilder(doc.ChildElements[0], "cda");
            var rule           = new Rule();

            rule.Context = contextBuilder.GetFullyQualifiedContextString();

            var assertionBuilder = new AssertionLineBuilder(doc.ChildElements[0].Attributes[0], templateIdentifierXpath, templateVersionIdentifierXpath);  //"OBS"

            rule.Assertions.Add(new Assertion()
            {
                AssertionMessage = "SHALL contain 1..1 @classCode='OBS' Observation (CodeSystem: HL7ActClass 2.16.840.1.113883.5.6) (CONF:8648).",
                Test             = assertionBuilder.WithCardinality(CardinalityParser.Parse("1..1")).WithinContext(contextBuilder.GetRelativeContextString()).ConformsTo(Conformance.SHALL).ToString()
            });

            assertionBuilder = new AssertionLineBuilder(doc.ChildElements[0].Attributes[1], templateIdentifierXpath, templateVersionIdentifierXpath);  //"EVN"
            rule.Assertions.Add(new Assertion()
            {
                AssertionMessage = "SHALL contain 1..1 @moodCode='EVN' Event (CodeSystem: ActMood 2.16.840.1.113883.5.1001) (CONF:8649).",
                Test             = assertionBuilder.WithCardinality(CardinalityParser.Parse("1..1")).WithinContext(contextBuilder.GetRelativeContextString()).ConformsTo(Conformance.SHALL).ToString()
            });
            var pattern = new Pattern();

            pattern.ID   = sectionCount.ToString();
            pattern.Name = string.Format("pattern-{0}-errors", pattern.ID);
            pattern.Rules.Add(rule);
            phase.ActivePatterns.Add(pattern);

            rule             = new Rule();
            contextBuilder   = new ContextBuilder(doc.ChildElements[1], "cda");
            rule.Context     = contextBuilder.GetFullyQualifiedContextString();
            assertionBuilder = new AssertionLineBuilder(doc.ChildElements[1], templateIdentifierXpath, templateVersionIdentifierXpath);  //"templateId[@rootCode]"
            rule.Assertions.Add(new Assertion()
            {
                AssertionMessage = "SHALL contain 1..1 @root='2.16.840.1.113883.10.20.22.4.48' (CONF:10485).",
                Test             = assertionBuilder.WithCardinality(CardinalityParser.Parse("1..1")).WithinContext(contextBuilder.GetRelativeContextString()).ConformsTo(Conformance.SHALL).ToString()
            });

            sectionCount++;
            pattern      = new Pattern();
            pattern.ID   = sectionCount.ToString();
            pattern.Name = string.Format("pattern-{0}-errors", pattern.ID);
            pattern.Rules.Add(rule);
            phase.ActivePatterns.Add(pattern);

            rule             = new Rule();
            contextBuilder   = new ContextBuilder(doc.ChildElements[2], "cda");
            rule.Context     = contextBuilder.GetFullyQualifiedContextString();
            assertionBuilder = new AssertionLineBuilder(doc.ChildElements[2], templateIdentifierXpath, templateVersionIdentifierXpath);  //"1..* id"
            rule.Assertions.Add(new Assertion()
            {
                AssertionMessage = "SHALL contain 1..* id (CONF:8654)",
                Test             = assertionBuilder.WithCardinality(CardinalityParser.Parse("1..*")).WithinContext(contextBuilder.GetRelativeContextString()).ConformsTo(Conformance.SHALL).ToString()
            });

            sectionCount++;
            pattern      = new Pattern();
            pattern.ID   = sectionCount.ToString();
            pattern.Name = string.Format("pattern-{0}-errors", pattern.ID);
            pattern.Rules.Add(rule);
            phase.ActivePatterns.Add(pattern);

            rule             = new Rule();
            contextBuilder   = new ContextBuilder(doc.ChildElements[3], "cda");
            rule.Context     = contextBuilder.GetFullyQualifiedContextString();
            assertionBuilder = new AssertionLineBuilder(doc.ChildElements[3], templateIdentifierXpath, templateVersionIdentifierXpath);  //"1..1 code @xsi:type='CE' valueset = 2.16.840.1.113883.1.11.20.2"
            rule.Assertions.Add(new Assertion()
            {
                AssertionMessage = "SHALL contain 1..1 code with @xsi:type='CE', where the @code SHOULD be selected from ValueSet AdvanceDirectiveTypeCode 2.16.840.1.113883.1.11.20.2 STATIC 2006-10-17 (CONF:8651).",
                Test             = assertionBuilder.WithCardinality(CardinalityParser.Parse("1..1")).WithinContext(contextBuilder.GetRelativeContextString()).ConformsTo(Conformance.SHALL).ToString()
            });

            sectionCount++;
            pattern      = new Pattern();
            pattern.ID   = sectionCount.ToString();
            pattern.Name = string.Format("pattern-{0}-errors", pattern.ID);
            pattern.Rules.Add(rule);
            phase.ActivePatterns.Add(pattern);

            rule             = new Rule();
            contextBuilder   = new ContextBuilder(doc.ChildElements[3], "cda");
            rule.Context     = contextBuilder.GetFullyQualifiedContextString();
            assertionBuilder = new AssertionLineBuilder(doc.ChildElements[3], templateIdentifierXpath, templateVersionIdentifierXpath);  //"1..1 statusCode @code='completed' valueset = 2.16.840.1.113883.1.11.20.2"
            rule.Assertions.Add(new Assertion()
            {
                AssertionMessage = "SHALL contain 1..1 code with @xsi:type='CE', where the @code SHOULD be selected from ValueSet AdvanceDirectiveTypeCode 2.16.840.1.113883.1.11.20.2 STATIC 2006-10-17 (CONF:8651).",
                Test             = assertionBuilder.WithCardinality(CardinalityParser.Parse("1..1")).WithinContext(contextBuilder.GetRelativeContextString()).ConformsTo(Conformance.SHALL).ToString()
            });

            sectionCount++;
            pattern      = new Pattern();
            pattern.ID   = sectionCount.ToString();
            pattern.Name = string.Format("pattern-{0}-errors", pattern.ID);
            pattern.Rules.Add(rule);
            phase.ActivePatterns.Add(pattern);

            rule           = new Rule();
            contextBuilder = new ContextBuilder(doc.ChildElements[1].Attributes[0], "cda");
            rule.Context   = contextBuilder.GetFullyQualifiedContextString();
            var childtemplateIdElementAssertionBuilder = new AssertionLineBuilder(templateIdElement.Attributes[0], templateIdentifierXpath, templateVersionIdentifierXpath)  //templateId/@root
                                                         .WithCardinality(CardinalityParser.Parse("1..1"))
                                                         .ConformsTo(Conformance.SHALL)
                                                         .WithinContext("cda:");
            var childParticipantElementAssertionBuilder = new AssertionLineBuilder(participantRoleElement, templateIdentifierXpath, templateVersionIdentifierXpath)
                                                          .WithCardinality(CardinalityParser.Parse("1..*"))
                                                          .ConformsTo(Conformance.SHALL)
                                                          .WithinContext("cda:");
            var childTimeElementAssertionBuilder = new AssertionLineBuilder(timeElement, templateIdentifierXpath, templateVersionIdentifierXpath)
                                                   .WithCardinality(CardinalityParser.Parse("0..1"))
                                                   .ConformsTo(Conformance.SHOULD)
                                                   .WithinContext("cda:");

            assertionBuilder = new AssertionLineBuilder(participantElement, templateIdentifierXpath, templateVersionIdentifierXpath);  //participant
            rule.Assertions.Add(new Assertion()
            {
                AssertionMessage = "should contain 1..* participant (CONF:8662), participant should contain 0..1 time (CONF:8665), the data type of Observation/participant/time in a verification SHALL be TS (time stamp) (CONF:8666), participant shall contain 1..1 participantRole (CONF:8825), participant shall contain 1..1 @typeCode=VRF 'Verifier' (CodeSystem: 2.16.840.1.113883.5.90) (CONF:8663), participant shall contain 1..1 templateId (CONF:8664), templateId shall contain 1..1 @root=2.16.840.1.113883.10.20.1.58 (CONF:10486)",
                Test             = assertionBuilder
                                   .WithCardinality(CardinalityParser.Parse("1..*"))
                                   .WithinContext("cda:")
                                   .ConformsTo(Conformance.SHALL)
                                   .WithChildElementBuilder(childTimeElementAssertionBuilder)
                                   .WithChildElementBuilder(childParticipantElementAssertionBuilder)
                                   .WithChildElementBuilder(childtemplateIdElementAssertionBuilder)
                                   .ToString()
            });

            sectionCount++;
            pattern      = new Pattern();
            pattern.ID   = sectionCount.ToString();
            pattern.Name = string.Format("pattern-{0}-errors", pattern.ID);
            pattern.Rules.Add(rule);
            phase.ActivePatterns.Add(pattern);

            var    builder         = new SchematronDocumentSerializer();
            string serializedModel = builder.SerializeDocument(document);

            Assert.IsFalse(string.IsNullOrEmpty(serializedModel), "No string returned from serialize document");

            string[] lModelLines = serializedModel.Split('\n');
            Assert.IsNotNull(lModelLines, "The generated string was not split on lines");
            Assert.IsTrue(lModelLines.Length > 1, "The generated string was not split on lines");
        }
        public void GenerateSchematronAssertion_MultipleNestedElement_ConformanceSHALL_AND_SHOULD_CardinalityOneToOne_AND_ZeroToOne_For_Advance_Directive_Number9Constraint()
        {
            var doc = new DocumentTemplate("cda");

            doc.AddElement(new DocumentTemplateElement("observation"));
            var participantElement = new DocumentTemplateElement("participant");

            participantElement.AddAttribute(new DocumentTemplateElementAttribute("typeCode", "CST"));
            var participantRoleElement = new DocumentTemplateElement("participantRole");

            participantRoleElement.AddAttribute(new DocumentTemplateElementAttribute("classCode", "AGNT"));
            participantElement.AddElement(participantRoleElement);
            var addrElement = new DocumentTemplateElement("addr");

            participantRoleElement.AddElement(addrElement);
            var telecomElement = new DocumentTemplateElement("telecom");

            participantRoleElement.AddElement(telecomElement);
            var playingEntityElement = new DocumentTemplateElement("playingEntity");

            participantRoleElement.AddElement(playingEntityElement);
            var nameElement = new DocumentTemplateElement("name");

            playingEntityElement.AddElement(nameElement);


            var participantRoleChildAssertionBuilder = new AssertionLineBuilder(this.tdb, participantRoleElement, this.igType, this.igTypeSchema)
                                                       .WithinContext("cda:")
                                                       .WithCardinality(CardinalityParser.Parse("1..1"))
                                                       .ConformsTo(Conformance.SHALL);
            var addrChildAssertionBuilder = new AssertionLineBuilder(this.tdb, addrElement, this.igType, this.igTypeSchema)
                                            .WithinContext("cda:")
                                            .WithCardinality(CardinalityParser.Parse("0..1"))
                                            .ConformsTo(Conformance.SHALL);
            var telecomChildAssertionBuilder = new AssertionLineBuilder(this.tdb, telecomElement, this.igType, this.igTypeSchema)
                                               .WithinContext("cda:")
                                               .WithCardinality(CardinalityParser.Parse("0..1"))
                                               .ConformsTo(Conformance.SHALL);
            var nameChildAssertionBuilder = new AssertionLineBuilder(this.tdb, nameElement, this.igType, this.igTypeSchema)
                                            .WithinContext("cda:")
                                            .WithCardinality(CardinalityParser.Parse("1..1"))
                                            .ConformsTo(Conformance.SHALL);
            var playingEntityChildAssertionBuilder = new AssertionLineBuilder(this.tdb, playingEntityElement, this.igType, this.igTypeSchema)
                                                     .WithinContext("cda:")
                                                     .WithCardinality(CardinalityParser.Parse("1..1"))
                                                     .WithChildElementBuilder(nameChildAssertionBuilder)                                                                 //nested child assertion builder
                                                     .ConformsTo(Conformance.SHALL);

            var assertionBuilder = new AssertionLineBuilder(this.tdb, participantElement, this.igType, this.igTypeSchema);  //participant
            var assertion        = assertionBuilder
                                   .WithCardinality(CardinalityParser.Parse("1..1"))
                                   .WithinContext("cda:")
                                   .ConformsTo(Conformance.SHALL)
                                   .WithChildElementBuilder(participantRoleChildAssertionBuilder)
                                   .WithChildElementBuilder(addrChildAssertionBuilder)
                                   .WithChildElementBuilder(telecomChildAssertionBuilder)
                                   .WithChildElementBuilder(playingEntityChildAssertionBuilder)
                                   .ToString();
            var expected = "count(cda:participant[@typeCode='CST'][count(cda:participantRole[@classCode='AGNT'])=1][count(cda:addr) < 2][count(cda:telecom) < 2][count(cda:playingEntity[count(cda:name)=1])=1])=1";

            Assert.IsTrue(assertion == expected, "Assertion string was not correct. Expected '{0}', Actual '{1}'", expected, assertion);
        }