public void GenerateSchematronAssertion_NestedElement_WithAttribute_NULLFlavor_ConformanceSHALL_CardinalityOneToOne() { var idElement = new DocumentTemplateElement("id"); var nullFlavorAttribute = new DocumentTemplateElementAttribute("nullFlavor", "NI"); idElement.AddAttribute(nullFlavorAttribute); var cdaDocumentTemplate = new DocumentTemplate("urn:hl7-org:v3"); cdaDocumentTemplate.AddElement(new DocumentTemplateElement("component") .AddElement(new DocumentTemplateElement("section") .AddElement(new DocumentTemplateElement("entry") .AddElement(new DocumentTemplateElement("organizer") .AddElement(new DocumentTemplateElement("performer") .AddElement(new DocumentTemplateElement("assignedEntity") .AddElement(idElement))))))); var builder = new AssertionLineBuilder(this.tdb, nullFlavorAttribute, this.igType, this.igTypeSchema); builder.ConformsTo(ConformanceParser.Parse("SHALL")).WithCardinality(CardinalityParser.Parse("1..1")).WithinContext("cda:"); var assertion = builder.ToString(); var expectedAssertion = @"cda:id[@nullFlavor='NI']"; Assert.IsTrue(assertion == expectedAssertion, "Assertion string was not correct. Expected '{0}', Actual '{1}'.", expectedAssertion, assertion); var contextBuilder = new ContextBuilder(idElement.ParentElement, "cda"); var context = contextBuilder.GetRelativeContextString(); var expectedContext = "cda:assignedEntity"; Assert.IsTrue(context == expectedContext, "Context is not correct. Expected '{0}', Actual '{1}'", expectedContext, context); }
public void GenerateSchematronAssertion_Element_With_Value_And_AttributeOverride_ConformanceSHALL_CardinalityOneToOne() { //create cda doc var cdaDocumentTemplate = new DocumentTemplate("urn:hl7-org:v3"); //create code element var element = new DocumentTemplateElement("statusCode", "Test"); element.ElementToAttributeOverrideMapping.Add("statusCode", "code"); element.ElementToAttributeOverrideMapping.Add("code", "code"); //add element to doc cdaDocumentTemplate.AddElement(element); //create schematron assertion line builder var builder = new AssertionLineBuilder(this.tdb, element, this.igType, this.igTypeSchema); //define cardinality and conformance var cardinality = CardinalityParser.Parse("1..1"); var conformance = ConformanceParser.Parse("SHALL"); //add element (context comes from doc), conformance, cardinality through fluent interface builder.ConformsTo(conformance).WithCardinality(cardinality); //generate string string assertion = builder.ToString(); //did we generate the correct string? Assert.IsTrue(assertion == @"count(cda:statusCode[@code='Test'])=1", "The generated assertion was not correct. Expected 'count(cda:statusCode[@code='Test'])=1', Actual '{0}'.", assertion); }
public void GenerateSchematronAssertion_Attribute_TwoAttributes_No_Element_ConformanceSHALL_CardinalityOneToOne() { var attr = new DocumentTemplateElementAttribute("code"); attr.SingleValue = "OBS"; attr.DataType = "CD"; //create schematron assertion line builder, build one at a time (regular interface, see above for fluent interface) var builder = new AssertionLineBuilder(this.tdb, attr, this.igType, this.igTypeSchema); //define cardinality var cardinality = CardinalityParser.Parse("1..1"); //add cardinality for the element builder.WithCardinality(cardinality); //define conformance var conformance = ConformanceParser.Parse("SHALL"); //add conformance for the element builder.ConformsTo(conformance); //generate string string assertion = builder.ToString(); //did we generate the correct string? string expected = @"count(self::node()[@code='OBS'])=1"; Assert.IsTrue(assertion == expected, "Assertion string was not correct. Expected '{0}', Actual '{1}'.", expected, assertion); }
static public void AddElementValueAndDataType(string aPrefix, DocumentTemplateElement aElement, IConstraint aTemplateConstraint) { if (!string.IsNullOrEmpty(aTemplateConstraint.Value)) { if (!string.IsNullOrEmpty(aTemplateConstraint.ValueConformance)) { var valueConformanceType = ConformanceParser.Parse(aTemplateConstraint.ValueConformance); var conformanceType = ConformanceParser.Parse(aTemplateConstraint.Conformance); if (valueConformanceType == conformanceType) { aElement.Value = !string.IsNullOrEmpty(aTemplateConstraint.Value) ? aTemplateConstraint.Value.Trim() : aTemplateConstraint.Value; } } else { aElement.Value = !string.IsNullOrEmpty(aTemplateConstraint.Value) ? aTemplateConstraint.Value.Trim() : aTemplateConstraint.Value; } } // Add the data type attribute if one is present on the constraint if (!string.IsNullOrEmpty(aTemplateConstraint.DataType)) { if (aPrefix == "cda") { if (aElement.ElementName != "code") { DocumentTemplateElementAttribute dataTypeAttr = new DocumentTemplateElementAttribute("xsi:type"); dataTypeAttr.SingleValue = aTemplateConstraint.DataType; aElement.AddAttribute(dataTypeAttr); } } } }
public void GenerateSchematronAssertion_Attribute_Value_DataType_ConformanceSHALL_CardinalityOneToOne() { var attr = new DocumentTemplateElementAttribute("code", "57024-2") { DataType = "CE" }; //create schematron assertion line builder, build one at a time (regular interface, see above for fluent interface) var builder = new AssertionLineBuilder(attr, templateIdentifierXpath, templateVersionIdentifierXpath); //define cardinality var cardinality = CardinalityParser.Parse("1..1"); //add cardinality for the element builder.WithCardinality(cardinality); //define conformance var conformance = ConformanceParser.Parse("SHALL"); //add conformance for the element builder.ConformsTo(conformance); //generate string string assertion = builder.ToString(); //did we generate the correct string? string expected = @"count(self::node()[@code='57024-2'])=1"; Assert.IsTrue(assertion == expected, "Assertion string was not correct. Expected '{0}', Actual '{1}'.", expected, assertion); }
public ConstraintParser(IObjectRepository tdb, IConstraint constraint, ImplementationGuideType igType, SimpleSchema igTypeSchema, IEnumerable <Template> allTemplates, string valueSetFile = "voc.xml", VocabularyOutputType vocabularyOutputType = VocabularyOutputType.Default) { this.tdb = tdb; this.constraint = constraint; this.valueSetFile = valueSetFile; this.igType = igType; this.igTypeSchema = igTypeSchema; this.allTemplates = allTemplates; this.igTypePlugin = igType.GetPlugin(); if (!string.IsNullOrEmpty(this.constraint.Cardinality)) { this.constraintCardinalityType = CardinalityParser.Parse(this.constraint.Cardinality); } if (!string.IsNullOrEmpty(this.constraint.Conformance)) { this.constraintConformanceType = ConformanceParser.Parse(this.constraint.Conformance); } this.prefix = igType.SchemaPrefix; this.vocabularyOutputType = vocabularyOutputType; }
static public void AddConformance(IConstraint aTemplateConstraint, AssertionLineBuilder aAssertionLineBuilder) { // Determine if we have a conformance if (!string.IsNullOrEmpty(aTemplateConstraint.Conformance)) { aAssertionLineBuilder.ConformsTo( ConformanceParser.Parse(aTemplateConstraint.Conformance)); } }
/// <summary> /// Helper function which creates an AssertionLineBuilder for an element given the constraint. This function will examine the constraint's value and datatype /// and add those to the builder if necessary. Also if aGenerateContext == true then it will add the context of the element. /// </summary> /// <param name="aElement">Element to base the AssertionLineBuilder from</param> /// <param name="aTemplateConstraint">TemplateConstraint which has the necessary properties (e.g. Element Value, Data Type) to add to the AssertionLineBuilder</param> /// <param name="aGenerateContext">Flag to determine whether a context should be added as part of the AssertionLineBuilder</param> /// <returns>A new AssertionLineBuilder for the aElement passed in</returns> private AssertionLineBuilder CreateAssertionLineBuilderForElement(DocumentTemplateElement aElement, IConstraint aTemplateConstraint, ref string aParentContext, bool aGenerateContext = true) { //add the value and data type ConstraintToDocumentElementHelper.AddElementValueAndDataType(this.prefix, aElement, aTemplateConstraint); //create builders var builder = new AssertionLineBuilder(this.tdb, aElement, this.igType, this.igTypeSchema); if (aGenerateContext) { ContextBuilder contextBuilder = null; if (aElement.ParentElement != null) //build the context { contextBuilder = new ContextBuilder(aElement.ParentElement, this.prefix); //the context will start from the first parent (or root) builder.WithinContext(string.Format("{0}", contextBuilder.GetFullyQualifiedContextString())); } } var containedTemplates = (from tcr in aTemplateConstraint.References join t in this.allTemplates on tcr.ReferenceIdentifier equals t.Oid where tcr.ReferenceType == ConstraintReferenceTypes.Template select new { Identifier = t.Oid, t.PrimaryContextType }); foreach (var containedTemplate in containedTemplates) { builder.ContainsTemplate(containedTemplate.Identifier, containedTemplate.PrimaryContextType); if (aTemplateConstraint.Parent != null && aTemplateConstraint.Parent.IsBranch) { builder.WithinContext(aParentContext, ContextWrapper.Slash); //put the parent context into the element context, this is a special case where we want the full context within the template assertion aParentContext = string.Empty; //clear the parent context b/c we have put it within the element's context } } if (aTemplateConstraint.Parent != null) { Conformance conformance = ConformanceParser.Parse(aTemplateConstraint.Parent.Conformance); if (conformance == Conformance.SHOULD) { builder.HasOptionalParentContext(); } } //TODO: Refactor this out, hardcoding these special cases for QRDA if (this.prefix.ToLower().Contains("cda")) { aElement.ElementToAttributeOverrideMapping.Add("code", "code"); aElement.ElementToAttributeOverrideMapping.Add("statusCode", "code"); aElement.ElementToAttributeOverrideMapping.Add("realmCode", "code"); aElement.ElementToAttributeOverrideMapping.Add("externalDocument", "classCode"); } return(builder); }
public void GenerateSchematronAssertion_Element_ValueSet_SVS_ConformanceSHALL_CardinalityOneToOne() { var element = new DocumentTemplateElement("administrativeGenderCode"); //create schematron assertion line builder, build one at a time (regular interface, see above for fluent interface) var builder = new AssertionLineBuilder(this.tdb, element, this.igType, this.igTypeSchema); //define cardinality var cardinality = CardinalityParser.Parse("1..1"); //add cardinality for the element builder.WithCardinality(cardinality); //define conformance var conformance = ConformanceParser.Parse("SHALL"); //add conformance for the element builder.ConformsTo(conformance); builder.WithinValueSet("4.3.2.1", VocabularyOutputType.SVS_SingleValueSet); //generate string string assertion = builder.ToString(); //did we generate the correct string? string expected = @"count(cda:administrativeGenderCode[@code=document('4.3.2.1.xml')/svs:RetrieveValueSetResponse/svs:ValueSet[@id='4.3.2.1']/svs:ConceptList/svs:Concept/@code])=1"; Assert.IsTrue(assertion == expected, "Assertion string was not correct. Expected '{0}', Actual '{1}'.", expected, assertion); }
public void GenerateSchematronAssertion_Element_With_Value_ConformanceSHALL_CardinalityOneToOne() { //create cda doc var cdaDocumentTemplate = new DocumentTemplate("urn:hl7-org:v3"); //create code element var element = new DocumentTemplateElement("code", "Test"); //add element to doc cdaDocumentTemplate.AddElement(element); //create schematron assertion line builder var builder = new AssertionLineBuilder(element, templateIdentifierXpath, templateVersionIdentifierXpath, "cda"); //define cardinality and conformance var cardinality = CardinalityParser.Parse("1..1"); var conformance = ConformanceParser.Parse("SHALL"); //add element (context comes from doc), conformance, cardinality through fluent interface builder.ConformsTo(conformance).WithCardinality(cardinality); //generate string string assertion = builder.ToString(); //did we generate the correct string? Assert.IsTrue(assertion == @"count(cda:code[translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='test'])=1", "The generated assertion was not correct. Expected 'count(cda:code[text()='Test'])=1', Actual '{0}'.", assertion); }
public void GenerateSchematronAssertion_NamespaceEMA_Element_Template_ConformanceSHALL_CardinalityOneToOne() { //create cda doc var cdaDocumentTemplate = new DocumentTemplate("urn:hl7-org:v3"); //create code element var element = new DocumentTemplateElement("encounter"); //add element to doc cdaDocumentTemplate.AddElement(element); //create schematron assertion line builder var builder = new AssertionLineBuilder(this.tdb, element, this.igType, this.igTypeSchema, "ema"); //define cardinality and conformance var cardinality = CardinalityParser.Parse("1..1"); var conformance = ConformanceParser.Parse("SHALL"); //add element (context comes from doc), conformance, cardinality through fluent interface builder.ConformsTo(conformance).WithCardinality(cardinality).ContainsTemplate("urn:oid:4.3.2.1", "Section"); //generate string string assertion = builder.ToString(); string expected = @"count(ema:encounter[ema:templateId[@root='4.3.2.1']])=1"; //did we generate the correct string? Assert.IsTrue(assertion == expected, "The generated assertion was not correct. Expected '{0}', Actual '{1}'.", expected, assertion); }
public void GenerateSchematronAssertion_Attribute_Valueset_ConformanceMAY_CardinalityOneToOne() { var attr = new DocumentTemplateElementAttribute("moodCode", "EVN"); //create schematron assertion line builder, build one at a time (regular interface, see above for fluent interface) var builder = new AssertionLineBuilder(this.tdb, attr, this.igType, this.igTypeSchema); //define cardinality var cardinality = CardinalityParser.Parse("1..1"); //add cardinality for the element builder.WithCardinality(cardinality); //define conformance var conformance = ConformanceParser.Parse("MAY"); //add conformance for the element builder.ConformsTo(conformance); //add valueset builder.WithinValueSet("2.16.840.1.113883.1.11.20.2"); //generate string string assertion = builder.ToString(); //did we generate the correct string? string expected = "@moodCode='EVN' and @moodCode=document('the_voc.xml')/voc:systems/voc:system[@valueSetOid='2.16.840.1.113883.1.11.20.2']/voc:code/@value"; Assert.IsTrue(assertion == expected, "Assertion string was not correct. Expected empty string, Actual '{1}'.", expected, assertion); }
public void GenerateSchematronAssertion_Element_ConformanceSHALL_CardinalityZeroToMany() { //create cda doc var cdaDocumentTemplate = new DocumentTemplate("urn:hl7-org:v3"); //create code element var element = new DocumentTemplateElement("code"); //add element to doc cdaDocumentTemplate.AddElement(element); //create schematron assertion line builder var builder = new AssertionLineBuilder(this.tdb, element, this.igType, this.igTypeSchema); //define cardinality and conformance var cardinality = CardinalityParser.Parse("0..*"); var conformance = ConformanceParser.Parse("SHALL"); //add element (context comes from doc), conformance, cardinality through fluent interface builder.ConformsTo(conformance).WithCardinality(cardinality); //generate string string assertion = builder.ToString(); string expected = "not(cda:code) or cda:code"; //did we generate the correct string? Assert.IsTrue(assertion == expected, "The generated assertion was not correct. Expected '{0}', Actual '{1}'.", expected, assertion); }
public ConstraintParser(IObjectRepository tdb, IConstraint constraint, ImplementationGuideType igType, string valueSetFile = "voc.xml", VocabularyOutputType vocabularyOutputType = VocabularyOutputType.Default) { this.tdb = tdb; this.constraint = constraint; this.valueSetFile = valueSetFile; this.igTypePlugin = igType.GetPlugin(); if (this.constraint.ValueSetId != null) { this.constraintValueSet = this.tdb.ValueSets.Single(y => y.Id == constraint.ValueSetId); } if (this.constraint.ValueCodeSystemId != null) { this.constraintCodeSystem = this.tdb.CodeSystems.Single(y => y.Id == constraint.ValueCodeSystemId); } if (!string.IsNullOrEmpty(this.constraint.Cardinality)) { this.constraintCardinalityType = CardinalityParser.Parse(this.constraint.Cardinality); } if (!string.IsNullOrEmpty(this.constraint.Conformance)) { this.constraintConformanceType = ConformanceParser.Parse(this.constraint.Conformance); } if (this.constraint.ContainedTemplateId != null) { this.containedTemplate = this.tdb.Templates.Single(y => y.Id == this.constraint.ContainedTemplateId.Value); } this.prefix = igType.SchemaPrefix; this.vocabularyOutputType = vocabularyOutputType; }
/// <summary> /// This is the main public interface for constraint parser. Takes the given constraint and builds an AssertionLineBuilder, applying the values, attributes, context, etc as necessary. /// </summary> /// <returns> /// AssertionLineBuilder representing the values from the constraint given to the parser. /// </returns> public AssertionLineBuilder CreateAssertionLineBuilder() { if (this.valueSet == null && this.constraint.ValueSetId != null) { this.valueSet = this.tdb.ValueSets.Single(y => y.Id == constraint.ValueSetId); } if (this.codeSystem == null && this.constraint.ValueCodeSystemId != null) { this.codeSystem = this.tdb.CodeSystems.Single(y => y.Id == constraint.ValueCodeSystemId); } IConstraint currentConstraint = this.constraint; //set current constraint, setting this as a variable allows us to move the current constraint to the parent when dealing with branches var containedTemplates = currentConstraint.References.Where(y => y.ReferenceType == ConstraintReferenceTypes.Template); if (string.IsNullOrEmpty(currentConstraint.Context) && !containedTemplates.Any()) //we can have empty context but a contained template { return(null); } DocumentTemplateElement element = null; DocumentTemplateElementAttribute attribute = null; ConstraintToDocumentElementHelper.ParseContextForElementAndAttribute(currentConstraint, out element, out attribute); DocumentTemplateElement parentElement = ConstraintToDocumentElementHelper.CreateParentElementForAttribute(currentConstraint, attribute); string parentContext = CreateParentContextForElement(parentElement, element, currentConstraint); AssertionLineBuilder asb = null; AssertionLineBuilder branchedRootAsb = null; // Determine if we should create the AssertionLineBuilder starting with an attribute or an element. if (attribute != null) { DecorateAttributeFromConstraint(parentContext, attribute, currentConstraint, this.valueSet); if (currentConstraint.Parent != null && currentConstraint.IsBranch) { branchedRootAsb = CreateBranchedRootAssertionLineBuilderFromConstraint(currentConstraint); } else { asb = CreateAssertionLineBuilderForAttribute(parentElement, attribute, ref parentContext, ref currentConstraint); } } else //this is an element { // Only add the code system constraints if there is no value conformance or the value conformance matches the element/attribute conformance // This is because in the SchematronGenerator class, a duplicate constraint is created when the value conformance is different from // the element/attribute conformance, where the duplicate constraint's conformance matches the value conformance. if (string.IsNullOrEmpty(currentConstraint.ValueConformance) || ConformanceParser.Parse(currentConstraint.Conformance) == ConformanceParser.Parse(currentConstraint.ValueConformance)) { ConstraintToDocumentElementHelper.AddCodeSystemToElement(this.tdb, this.igTypePlugin, element, currentConstraint); } if (currentConstraint.IsBranch) { branchedRootAsb = CreateBranchedRootAssertionLineBuilderFromConstraint(currentConstraint); branchedRootAsb.HasParentContext(parentContext); } else { //if we have a context already then we will append it at end so pass in false, else go ahead and generate (pass in true) asb = CreateAssertionLineBuilderForElement(element, this.constraint, ref parentContext, string.IsNullOrEmpty(parentContext)); } } if (branchedRootAsb == null) //if this is a branched root then a separate builder was constructed { ConstraintToDocumentElementHelper.AddConformance(currentConstraint, asb); ConstraintToDocumentElementHelper.AddCardinality(currentConstraint, asb); // Determine if we have a valueset if (this.valueSet != null && currentConstraint.IsValueSetStatic) { var requireValueset = true; if (!string.IsNullOrEmpty(currentConstraint.ValueConformance)) { if (ConformanceParser.Parse(currentConstraint.Conformance) != ConformanceParser.Parse(currentConstraint.ValueConformance)) { requireValueset = false; } } if (requireValueset) { //TODO: Move into CDA specific library //are we bound directly to a code or value element? bool includeNullFlavor = false; if (element != null && attribute == null && (element.ElementName == "value" || element.ElementName == "code")) { includeNullFlavor = true; } string valueSetIdentifier = this.igTypePlugin.ParseIdentifier(this.valueSet.GetIdentifier(this.igTypePlugin)); asb.WithinValueSet(valueSetIdentifier, this.valueSetFile, this.vocabularyOutputType, includeNullFlavor); } } //determine if we have a parent context if (!string.IsNullOrEmpty(parentContext)) { asb.HasParentContext(parentContext); DecorateParentOptionality(asb, currentConstraint); } } else //branched root, use that one instead { //determine if we have a parent context if (!string.IsNullOrEmpty(parentContext)) { branchedRootAsb.HasParentContext(parentContext); DecorateParentOptionality(branchedRootAsb, currentConstraint); } } return((branchedRootAsb == null) ? asb : branchedRootAsb); }