Example #1
0
        private static IEnumerable <MissingValidationIssue> VerifyMaxOrMinValueIsSet(Questionnaire.ItemComponent item)
        {
            if (!(item.Type == Questionnaire.QuestionnaireItemType.Date ||
                  item.Type == Questionnaire.QuestionnaireItemType.DateTime ||
                  item.Type == Questionnaire.QuestionnaireItemType.Time ||
                  item.Type == Questionnaire.QuestionnaireItemType.Integer ||
                  item.Type == Questionnaire.QuestionnaireItemType.Decimal))
            {
                return(new MissingValidationIssue[0]);
            }

            // If this is a read-only field and it contains a fhirpath for data extraction do not suggest any validation
            if (item.ReadOnly == true && item.GetExtensions(Constants.FhirPathUri).Any())
            {
                return(new MissingValidationIssue[0]);
            }

            var issues = new List <MissingValidationIssue>();

            if (item.GetExtensions(Constants.MaxValueUri).Count() == 0 || item.GetExtensions(Constants.MinValueUri).Count() == 0)
            {
                issues.Add(new MissingValidationIssue
                {
                    LinkId   = item.LinkId,
                    Severity = MissingValidationSeverityEnum.Information,
                    Details  = $"Consider setting the 'maxValue' and 'minValue' attribute for items of type '{item.Type}'."
                });
            }

            return(issues);
        }
Example #2
0
        private int DiveV1(int index, int level, List <Questionnaire.ItemComponent> itemComponents, QuestionnaireItem[] questionnaireItems)
        {
            int currentIndex = index;

            Questionnaire.ItemComponent item = null;
            for (; currentIndex < questionnaireItems.Length; currentIndex++)
            {
                QuestionnaireItem questionnaireItem = questionnaireItems[currentIndex];
                _logger.LogDebug($"Questionnaire Item: {questionnaireItem.LinkId} - {questionnaireItem.Type} - {questionnaireItem.Text}");

                int currentLevel = questionnaireItem.LinkId.Split('.').Length - 1;
                if (currentLevel == level)
                {
                    item = CreateItemComponentV1(questionnaireItem);
                    itemComponents.Add(item);
                }
                else if (currentLevel > level)
                {
                    if (item == null)
                    {
                        throw new Exception("LinkId cannot bypass a level, i.e. jumping from 1.1 to 1.1.1.1");
                    }
                    currentIndex = DiveV1(currentIndex, currentLevel, item.Item, questionnaireItems);
                }
                else if (currentLevel < level)
                {
                    // If current level is less than the entry level then break out of loop and return from recursion level.
                    break;
                }
            }
            return(currentIndex - 1);
        }
        private static IEnumerable <Issue> VerifyRepeatingItemValidation(Questionnaire.ItemComponent item)
        {
            var issues = new List <Issue>();

            if (item.Repeats == true)
            {
                if (item.GetExtensions(Constants.MaxOccursUri).Count() == 0)
                {
                    issues.Add(new Issue
                    {
                        LinkId   = item.LinkId,
                        Severity = IssueSeverityEnum.Warning,
                        Details  = $"An item where the attribute 'repeats' is set to 'true' a 'maxOccurs' is recommended to be set."
                    });
                }
            }

            return(issues);
        }
        private static IEnumerable <Issue> VerifyAttachmentValidation(Questionnaire.ItemComponent item)
        {
            if (item.Type != Questionnaire.QuestionnaireItemType.Attachment)
            {
                return(new Issue[0]);
            }

            var issues = new List <Issue>();

            if (item.GetExtensions(Constants.QuestionnaireAttachmentMaxSize).Count() == 0)
            {
                issues.Add(new Issue
                {
                    LinkId   = item.LinkId,
                    Severity = IssueSeverityEnum.Warning,
                    Details  = $"An item of type '{item.Type}' is missing the 'maxSize' attribute. Consider setting the 'maxSize' attribute."
                });
            }

            return(issues);
        }
Example #5
0
        public static bool IsItemControlOfType(this Questionnaire.ItemComponent item, params string[] itemControlType)
        {
            EnsureArg.IsNotNull(itemControlType, nameof(itemControlType));

            IEnumerable <Extension> extensions = item.GetExtensions(Constants.ItemControlUri);

            foreach (Extension extension in extensions)
            {
                if (!(extension.Value is CodeableConcept codeableConcept))
                {
                    continue;
                }
                foreach (Coding coding in codeableConcept.Coding)
                {
                    if (itemControlType.Contains(coding.Code))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Example #6
0
        /// <summary>Builds a questionnaire.</summary>
        /// <exception cref="ArgumentNullException">Thrown when one or more required arguments are null.</exception>
        /// <param name="format">Describes the format to use.</param>
        /// <returns>A Questionnaire.</returns>
        private static Questionnaire BuildQuestionnaire(
            IReportingFormat format)
        {
            if (format == null)
            {
                throw new ArgumentNullException(nameof(format));
            }

            if (string.IsNullOrEmpty(format.Name))
            {
                throw new ArgumentNullException(nameof(format), $"Invalid IReportingFormat.Name: {format.Name}");
            }

            if ((format.Fields == null) || (format.Fields.Count == 0))
            {
                throw new ArgumentNullException(nameof(format), $"Invalid IReportingFormat.Fields: {format.Fields}");
            }

            if ((format.QuestionnaireSections == null) || (format.QuestionnaireSections.Count == 0))
            {
                throw new ArgumentNullException(nameof(format), $"Invalid IReportingFormat.QuestionnaireSections: {format.QuestionnaireSections}");
            }

            Questionnaire questionnaire = new Questionnaire()
            {
                Meta = new Meta()
                {
                    Profile = new string[]
                    {
                        FhirSystems.Questionnaire,
                    },
                },
                Id           = format.Name,
                Name         = format.Name,
                Url          = $"{CanonicalUrl}/Questionnaire/{format.Name}",
                Version      = QuestionnaireVersion,
                Title        = format.Title,
                Description  = new Markdown(format.Description),
                Status       = PublicationStatus.Draft,
                Date         = PublicationDate,
                Publisher    = Publisher,
                Jurisdiction = new List <CodeableConcept>()
                {
                    FhirTriplet.UnitedStates.GetConcept(),
                },
                UseContext = new List <UsageContext>()
                {
                    new UsageContext()
                    {
                        Code  = FhirTriplet.GetCode(FhirSystems.UsageContextType, CommonLiterals.ContextFocus),
                        Value = FhirTriplet.SctCovid.GetConcept(),
                    },
                },
                Item = new List <Questionnaire.ItemComponent>(),
            };

            int sectionNumber = -1;
            int itemNumber    = 0;

            foreach (QuestionnaireSection questionnaireSection in format.QuestionnaireSections)
            {
                sectionNumber++;
                itemNumber = 0;

                Questionnaire.ItemComponent section = new Questionnaire.ItemComponent()
                {
                    LinkId  = $"section_{sectionNumber}",
                    Type    = Questionnaire.QuestionnaireItemType.Group,
                    Item    = new List <Questionnaire.ItemComponent>(),
                    Repeats = false,
                };

#if false   // 2020.05.13 - Argonaut extensions aren't valid in R4 yet
                section.AddExtension(
                    "http://fhir.org/guides/argonaut/questionnaire/StructureDefinition/extension-itemOrder",
                    new FhirDecimal(sectionNumber));
#endif

                if (format.Fields.ContainsKey(questionnaireSection.Title))
                {
                    section.Text = $"{format.Fields[questionnaireSection.Title].Title}: {format.Fields[questionnaireSection.Title].Description}";
                }
                else
                {
                    section.Text = questionnaireSection.Title;
                }

                foreach (QuestionnaireQuestion question in questionnaireSection.Fields)
                {
                    Questionnaire.ItemComponent component = ComponentFromQuestion(
                        format,
                        question,
                        ref itemNumber);

                    if (component == null)
                    {
                        continue;
                    }

                    section.Item.Add(component);
                }

                questionnaire.Item.Add(section);
            }

            return(questionnaire);
        }
Example #7
0
        /// <summary>Component from question.</summary>
        /// <param name="format">   Describes the format to use.</param>
        /// <param name="question"> The question.</param>
        /// <param name="itemOrder">[in,out] The item order.</param>
        /// <returns>A Questionnaire.ItemComponent.</returns>
        private static Questionnaire.ItemComponent ComponentFromQuestion(
            IReportingFormat format,
            QuestionnaireQuestion question,
            ref int itemOrder)
        {
            if (!format.Fields.ContainsKey(question.ValueFieldName))
            {
                return(null);
            }

            FormatField valueField = format.Fields[question.ValueFieldName];
            FormatField displayField;

            if (string.IsNullOrEmpty(question.DisplayFieldName) ||
                (!format.Fields.ContainsKey(question.DisplayFieldName)))
            {
                displayField = valueField;
            }
            else
            {
                displayField = format.Fields[question.DisplayFieldName];
            }

            Questionnaire.ItemComponent component = new Questionnaire.ItemComponent()
            {
                LinkId   = valueField.Name,
                Required = valueField.IsRequired == true,
                Repeats  = false,
            };

            if (!string.IsNullOrEmpty(question.FieldSystem))
            {
                component.Code = new List <Coding>()
                {
                    new Coding(question.FieldSystem, valueField.Name),
                };
            }

#if false   // 2020.05.13 - Argonaut extensions aren't valid in R4 yet
            component.AddExtension(
                "http://fhir.org/guides/argonaut/questionnaire/StructureDefinition/extension-itemOrder",
                new FhirDecimal(itemOrder++));
#endif

            if (question.UseTitleOnly)
            {
                component.Text = $"{displayField.Title}";
            }
            else
            {
                component.Text = $"{displayField.Title}: {displayField.Description}";
            }

            int optionOrder = 0;

            switch (valueField.Type)
            {
            case FormatField.FieldType.Date:
                component.Type = Questionnaire.QuestionnaireItemType.Date;
                break;

            case FormatField.FieldType.Count:
                component.Type = Questionnaire.QuestionnaireItemType.Integer;
                break;

            case FormatField.FieldType.Percentage:
                component.Type = Questionnaire.QuestionnaireItemType.Decimal;
                break;

            case FormatField.FieldType.Boolean:
                component.Type = Questionnaire.QuestionnaireItemType.Boolean;
                break;

            case FormatField.FieldType.Choice:
                component.Type         = Questionnaire.QuestionnaireItemType.Choice;
                component.AnswerOption = new List <Questionnaire.AnswerOptionComponent>();

                component.AddExtension(
                    "http://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive",
                    new FhirBoolean(true),
                    true);

                foreach (FormatFieldOption option in valueField.Options)
                {
                    Element element = new FhirString(option.Text);

#if false   // 2020.05.13 - Argonaut extensions aren't valid in R4 yet
                    element.AddExtension(
                        "http://fhir.org/guides/argonaut/questionnaire/StructureDefinition/extension-itemOrder",
                        new FhirDecimal(optionOrder++));
#endif

                    component.AnswerOption.Add(new Questionnaire.AnswerOptionComponent()
                    {
                        Value = element,
                    });
                }

                break;

            case FormatField.FieldType.MultiSelectChoice:
                component.Type         = Questionnaire.QuestionnaireItemType.Choice;
                component.AnswerOption = new List <Questionnaire.AnswerOptionComponent>();

                foreach (FormatFieldOption option in valueField.Options)
                {
                    Element element = new FhirString(option.Text);

#if false   // 2020.05.13 - Argonaut extensions aren't valid in R4 yet
                    element.AddExtension(
                        "http://fhir.org/guides/argonaut/questionnaire/StructureDefinition/extension-itemOrder",
                        new FhirDecimal(optionOrder++));
#endif

                    component.AnswerOption.Add(new Questionnaire.AnswerOptionComponent()
                    {
                        Value = element,
                    });
                }

                break;

            case FormatField.FieldType.Text:
                component.Type = Questionnaire.QuestionnaireItemType.Text;
                break;

            case FormatField.FieldType.ShortString:
                component.Type = Questionnaire.QuestionnaireItemType.String;
                break;

            case FormatField.FieldType.Display:
            default:
                component.Type = Questionnaire.QuestionnaireItemType.Display;
                break;
            }

            return(component);
        }
Example #8
0
        private static IEnumerable <MissingValidationIssue> VerifyOptionsReferenceExists(Questionnaire questionnaire, Questionnaire.ItemComponent item)
        {
            if (item.Type != Questionnaire.QuestionnaireItemType.Choice && item.Type != Questionnaire.QuestionnaireItemType.OpenChoice)
            {
                return(new MissingValidationIssue[0]);
            }
            if (item.Options == null)
            {
                return(new MissingValidationIssue[0]);
            }

            var issues = new List <MissingValidationIssue>();

            if (!questionnaire.HasContainedValueSet(item.Options))
            {
                issues.Add(new MissingValidationIssue
                {
                    LinkId   = item.LinkId,
                    Severity = MissingValidationSeverityEnum.Error,
                    Details  = $"Cannot find the reference to ValueSet {item.Options?.Reference}"
                });
            }

            return(issues);
        }
Example #9
0
        public static IEnumerable <MissingValidationIssue> VerifyItemValidation(this Questionnaire questionnaire, Questionnaire.ItemComponent item)
        {
            EnsureArg.IsNotNull(item, nameof(item));

            var issues = new List <MissingValidationIssue>();

            issues.AddRange(VerifyStringAndTextValidation(item));
            issues.AddRange(VerifyRepeatingItemValidation(item));
            issues.AddRange(VerifyMaxOrMinValueIsSet(item));
            issues.AddRange(VerifyAttachmentValidation(item));
            issues.AddRange(VerifyOptionsReferenceExists(questionnaire, item));

            foreach (Questionnaire.ItemComponent itm in item.Item)
            {
                issues.AddRange(questionnaire.VerifyItemValidation(itm));
            }

            return(issues);
        }
Example #10
0
        private static IEnumerable <MissingValidationIssue> VerifyStringAndTextValidation(Questionnaire.ItemComponent item)
        {
            if ((item.Type != Questionnaire.QuestionnaireItemType.String && item.Type != Questionnaire.QuestionnaireItemType.Text) ||
                !item.IsItemControlOfType("help", "help-link", "inline"))
            {
                return(new MissingValidationIssue[0]);
            }
            // If this is a read-only field and it contains a fhirpath for data extraction do not suggest any validation
            if (item.ReadOnly == true && item.GetExtensions(Constants.FhirPathUri).Any())
            {
                return(new MissingValidationIssue[0]);
            }

            var issues = new List <MissingValidationIssue>();

            // Issues an error if a maxLength has NOT been set
            if (!item.MaxLength.HasValue)
            {
                issues.Add(new MissingValidationIssue
                {
                    LinkId   = item.LinkId,
                    Severity = MissingValidationSeverityEnum.Error,
                    Details  = $"An item of type '{item.Type}' must have the 'maxLength' attribute set."
                });
            }
            // Issues an error if a regex has NOT been set
            if (item.GetExtensions(Constants.RegexUri).Count() == 0)
            {
                issues.Add(new MissingValidationIssue
                {
                    LinkId   = item.LinkId,
                    Severity = MissingValidationSeverityEnum.Error,
                    Details  = $"An item of type '{item.Type}' must have the 'regex' attribute set."
                });
            }
            if (item.Type == Questionnaire.QuestionnaireItemType.String)
            {
                // Issues an error for ItemType == 'string' if a maxLength of no more than 250 characters has been set.
                if (item.MaxLength.HasValue && item.MaxLength.Value > 250)
                {
                    issues.Add(new MissingValidationIssue
                    {
                        LinkId   = item.LinkId,
                        Severity = MissingValidationSeverityEnum.Error,
                        Details  = $"An item of type '{item.Type}' must have a 'maxLength' of no more than 250 characters."
                    });
                }
            }
            if (item.Type == Questionnaire.QuestionnaireItemType.Text)
            {
                // Issues a warning for ItemType == 'text' if a maxLength of more than 2500 characters has been set.
                if (item.MaxLength.HasValue && item.MaxLength.Value > 2500)
                {
                    issues.Add(new MissingValidationIssue
                    {
                        LinkId   = item.LinkId,
                        Severity = MissingValidationSeverityEnum.Warning,
                        Details  = $"Item field of '{item.Type}' has a 'maxLength' of more than 2500 characters. This is allowed, but not recommended."
                    });
                }
            }

            return(issues);
        }
Example #11
0
        private IList <Questionnaire> GetQuestionnairesFromFlatFileFormatV2(string path)
        {
            if (string.IsNullOrWhiteSpace(path))
            {
                throw new ArgumentNullException(nameof(path));
            }
            if (!File.Exists(path))
            {
                throw new FileNotFoundException($"File not found: '{path}'", path);
            }

            IList <Questionnaire> questionnaires = new List <Questionnaire>();

            var engine = new MasterDetailEngine <QuestionnaireHeader2, QuestionnaireItem2>(new MasterDetailSelector(RecordSelector))
            {
                Encoding = new UTF8Encoding()
            };

            MasterDetails <QuestionnaireHeader2, QuestionnaireItem2>[] masterDetails = engine.ReadFile(path);
            foreach (MasterDetails <QuestionnaireHeader2, QuestionnaireItem2> masterDetail in masterDetails)
            {
                _logger.LogDebug($"Questionnaire: {masterDetail.Master.Name} - {masterDetail.Master.Title}");

                Questionnaire questionnaire = new Questionnaire
                {
                    Meta = new Meta
                    {
                        Profile = new string[] { Constants.QuestionnaireProfileUri }
                    },

                    Id      = string.IsNullOrWhiteSpace(masterDetail.Master.Id) ? null : masterDetail.Master.Id,
                    Url     = string.IsNullOrWhiteSpace(masterDetail.Master.Url) ? null : masterDetail.Master.Url,
                    Version = string.IsNullOrWhiteSpace(masterDetail.Master.Version) ? null : masterDetail.Master.Version,
                    Name    = string.IsNullOrWhiteSpace(masterDetail.Master.Name) ? null : masterDetail.Master.Name,
                    Title   = string.IsNullOrWhiteSpace(masterDetail.Master.Title) ? null : masterDetail.Master.Title,
                    Status  = string.IsNullOrWhiteSpace(masterDetail.Master.Status) ? null : EnumUtility.ParseLiteral <PublicationStatus>(masterDetail.Master.Status)
                };
                if (!string.IsNullOrWhiteSpace(masterDetail.Master.Date))
                {
                    if (!DateUtility.IsValidFhirDateTime(masterDetail.Master.Date))
                    {
                        throw new Exception($"The date {masterDetail.Master.Date} is not conforming to the expected format: 'yyyy-MM-dd'");
                    }
                    questionnaire.DateElement = new FhirDateTime(masterDetail.Master.Date);
                }
                questionnaire.Publisher   = string.IsNullOrWhiteSpace(masterDetail.Master.Publisher) ? null : masterDetail.Master.Publisher;
                questionnaire.Description = string.IsNullOrWhiteSpace(masterDetail.Master.Description) ? null : new Markdown(masterDetail.Master.Description);
                questionnaire.Purpose     = string.IsNullOrWhiteSpace(masterDetail.Master.Purpose) ? null : new Markdown(masterDetail.Master.Purpose);
                if (!string.IsNullOrWhiteSpace(masterDetail.Master.ApprovalDate))
                {
                    if (!DateUtility.IsValidFhirDate(masterDetail.Master.ApprovalDate))
                    {
                        throw new Exception($"The date {masterDetail.Master.ApprovalDate} is not conforming to the expected format: 'yyyy-MM-dd'");
                    }
                    questionnaire.ApprovalDateElement = new Date(masterDetail.Master.ApprovalDate);
                }
                if (!string.IsNullOrWhiteSpace(masterDetail.Master.ApprovalDate))
                {
                    if (!DateUtility.IsValidFhirDate(masterDetail.Master.LastReviewDate))
                    {
                        throw new Exception($"The date {masterDetail.Master.LastReviewDate} is not conforming to the expected format: 'yyyy-MM-dd'");
                    }
                    questionnaire.LastReviewDateElement = new Date(masterDetail.Master.LastReviewDate);
                }
                questionnaire.Contact = string.IsNullOrWhiteSpace(masterDetail.Master.ContactName) ? null : new List <ContactDetail> {
                    new ContactDetail {
                        Name = masterDetail.Master.ContactName
                    }
                };
                questionnaire.Copyright = string.IsNullOrWhiteSpace(masterDetail.Master.Copyright) ? null : new Markdown(masterDetail.Master.Copyright);

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.SubjectType))
                {
                    IList <ResourceType?> resourceTypes = new List <ResourceType?>();
                    string[] subjectTypes = masterDetail.Master.SubjectType.Split('|');
                    foreach (string subjectType in subjectTypes)
                    {
                        ResourceType?resourceType = EnumUtility.ParseLiteral <ResourceType>(subjectType);
                        if (resourceType.HasValue)
                        {
                            resourceTypes.Add(resourceType);
                        }
                    }
                    questionnaire.SubjectType = resourceTypes;
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.Language))
                {
                    questionnaire.Language = masterDetail.Master.Language;
                    string displayName = CultureInfo.GetCultureInfo(LanguageCodeUtility.GetLanguageCode(questionnaire.Language))?.NativeName.UpperCaseFirstCharacter();
                    questionnaire.Meta.Tag.Add(new Coding("urn:ietf:bcp:47", questionnaire.Language, displayName == null ? string.Empty : displayName));
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.UseContext))
                {
                    questionnaire.UseContext = ParseUsageContext(masterDetail.Master.UseContext);
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.Endpoint))
                {
                    questionnaire.SetExtension(Constants.EndPointUri, new ResourceReference(masterDetail.Master.Endpoint));
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.AuthenticationRequirement))
                {
                    questionnaire.SetExtension(Constants.AuthenticationRequirementUri, new Coding(Constants.AuthenticationRequirementSystem, masterDetail.Master.AuthenticationRequirement));
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.AccessibilityToResponse))
                {
                    questionnaire.SetExtension(Constants.AccessibilityToResponseUri, new Coding(Constants.AccessibilityToResponseSystem, masterDetail.Master.AccessibilityToResponse));
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.CanBePerformedBy))
                {
                    questionnaire.SetExtension(Constants.CanBePerformedByUri, new Coding(Constants.CanBePerformedBySystem, masterDetail.Master.CanBePerformedBy));
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.Discretion))
                {
                    questionnaire.SetExtension(Constants.DiscretionUri, new Coding(Constants.DiscretionSystem, masterDetail.Master.Discretion));
                }

                if (masterDetail.Master.GeneratePdf.HasValue)
                {
                    questionnaire.SetExtension(Constants.GeneratePdfUri, new FhirBoolean(masterDetail.Master.GeneratePdf.Value));
                }
                else
                {
                    questionnaire.SetExtension(Constants.GeneratePdfUri, new FhirBoolean(true));
                }

                if (masterDetail.Master.GenerateNarrative.HasValue)
                {
                    questionnaire.SetExtension(Constants.GenerateNarrativeUri, new FhirBoolean(masterDetail.Master.GenerateNarrative.Value));
                }
                else
                {
                    questionnaire.SetExtension(Constants.GenerateNarrativeUri, new FhirBoolean(true));
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.PresentationButtons))
                {
                    questionnaire.SetExtension(Constants.PresentationButtonsUri, new Coding(Constants.PresentationButtonsSystem, masterDetail.Master.PresentationButtons));
                }
                else
                {
                    questionnaire.SetExtension(Constants.PresentationButtonsUri, new Coding(Constants.PresentationButtonsSystem, "sticky"));
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.Code))
                {
                    questionnaire.Code = ParseArrayOfCoding(masterDetail.Master.Code);
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.QuestionaireNavigatorState))
                {
                    questionnaire.SetExtension(Constants.QuestionaireNavigatorStateUri, new CodeableConcept {
                        Coding = new List <Coding> {
                            new Coding {
                                System = Constants.NavigatorStateSystem, Code = masterDetail.Master.QuestionaireNavigatorState,
                            }
                        }
                    });
                }

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.QuestionnairePrintVersionReference))
                {
                    questionnaire.SetExtension(Constants.QuestionnairePrintVersionUri, new ResourceReference(masterDetail.Master.QuestionnairePrintVersionReference));
                }

                IList <string> linkIds           = new List <string>();
                Questionnaire.ItemComponent item = null;
                for (int i = 0; i < masterDetail.Details.Length; i++)
                {
                    QuestionnaireItem2 questionnaireItem = masterDetail.Details[i];

                    if (linkIds.IndexOf(questionnaireItem.LinkId) > 0)
                    {
                        throw new DuplicateLinkIdException(questionnaireItem.LinkId);
                    }

                    _logger.LogDebug($"Questionnaire Item: {questionnaireItem.LinkId} - {questionnaireItem.Type} - {questionnaireItem.Text}");

                    int level = questionnaireItem.LinkId.Split('.').Length - 1;
                    if (level > 0)
                    {
                        // item could be null if a line in the .txt-file is empty or blank
                        if (item == null)
                        {
                            continue;
                        }

                        i = DiveV2(i, level, item.Item, masterDetail.Details);
                    }
                    else
                    {
                        item = CreateItemComponentV2(questionnaireItem);
                        questionnaire.Item.Add(item);
                    }
                }

                questionnaires.Add(questionnaire);
            }

            return(questionnaires);
        }
Example #12
0
        private static Questionnaire.ItemComponent CreateItemComponentV1(QuestionnaireItem item)
        {
            Questionnaire.QuestionnaireItemType?itemType = EnumUtility.ParseLiteral <Questionnaire.QuestionnaireItemType>(item.Type);
            if (!itemType.HasValue)
            {
                throw new Exception($"QuestionnaireItemType at question with linkId: {item.LinkId} is not conforming to any valid literals. QuestionnaireItemType: {item.Type}");
            }

            Questionnaire.ItemComponent itemComponent = new Questionnaire.ItemComponent
            {
                Type = itemType,
            };

            itemComponent.LinkId = string.IsNullOrWhiteSpace(item.LinkId) ? null : item.LinkId;
            itemComponent.Prefix = string.IsNullOrWhiteSpace(item.Prefix) ? null : item.Prefix;
            itemComponent.Text   = string.IsNullOrWhiteSpace(item.Text) ? null : item.Text;
            if (!string.IsNullOrWhiteSpace(item.EnableWhen))
            {
                itemComponent.EnableWhen = ParseEnableWhen(item.EnableWhen).ToList();
                // TODO: Defaults to 'any' in the first iteration of "migrate to R4".
                itemComponent.EnableBehavior = Questionnaire.EnableWhenBehavior.Any;
            }

            if (itemType != Questionnaire.QuestionnaireItemType.Group && itemType != Questionnaire.QuestionnaireItemType.Display)
            {
                itemComponent.Required = item.Required.HasValue ? item.Required : null;
                itemComponent.ReadOnly = item.ReadOnly;
                itemComponent.Initial  = string.IsNullOrEmpty(item.Initial)
                    ? null
                    : new List <Questionnaire.InitialComponent> {
                    new Questionnaire.InitialComponent {
                        Value = GetElement(itemType.Value, item.Initial)
                    }
                };
            }

            if (itemType != Questionnaire.QuestionnaireItemType.Display)
            {
                itemComponent.Repeats = item.Repeats;
            }

            if (!string.IsNullOrWhiteSpace(item.ValidationText))
            {
                itemComponent.SetStringExtension(Constants.ValidationTextUri, item.ValidationText);
            }
            if (!string.IsNullOrWhiteSpace(item.ReferenceValue) && item.ReferenceValue.IndexOf('#') == 0)
            {
                itemComponent.AnswerValueSetElement = new Canonical($"#{item.ReferenceValue.Substring(1)}");
            }
            if (!string.IsNullOrWhiteSpace(item.EntryFormat))
            {
                itemComponent.SetStringExtension(Constants.EntryFormatUri, item.EntryFormat);
            }
            if (item.MaxValue.HasValue)
            {
                itemComponent.SetIntegerExtension(Constants.MaxValueUri, item.MaxValue.Value);
            }
            if (item.MinValue.HasValue)
            {
                itemComponent.SetIntegerExtension(Constants.MinValueUri, item.MinValue.Value);
            }

            return(itemComponent);
        }
Example #13
0
        private IList <Questionnaire> GetQuestionnairesFromFlatFileFormatV1(string path)
        {
            if (string.IsNullOrWhiteSpace(path))
            {
                throw new ArgumentNullException(nameof(path));
            }
            if (!File.Exists(path))
            {
                throw new FileNotFoundException($"File not found: '{path}'", path);
            }

            IList <Questionnaire> questionnaires = new List <Questionnaire>();

            var engine = new MasterDetailEngine <QuestionnaireHeader, QuestionnaireItem>(new MasterDetailSelector(RecordSelector))
            {
                Encoding = new UTF8Encoding()
            };

            MasterDetails <QuestionnaireHeader, QuestionnaireItem>[] masterDetails = engine.ReadFile(path);
            foreach (MasterDetails <QuestionnaireHeader, QuestionnaireItem> masterDetail in masterDetails)
            {
                _logger.LogDebug($"Questionnaire: {masterDetail.Master.Name} - {masterDetail.Master.Title}");

                Questionnaire questionnaire = new Questionnaire();

                questionnaire.Meta = new Meta
                {
                    Profile = new string[] { "http://ehelse.no/fhir/StructureDefinition/sdf-Questionnaire" }
                };

                questionnaire.Id          = string.IsNullOrWhiteSpace(masterDetail.Master.Id) ? null : masterDetail.Master.Id;
                questionnaire.Url         = string.IsNullOrWhiteSpace(masterDetail.Master.Url) ? null : masterDetail.Master.Url;
                questionnaire.Version     = string.IsNullOrWhiteSpace(masterDetail.Master.Version) ? null : masterDetail.Master.Version;
                questionnaire.Name        = string.IsNullOrWhiteSpace(masterDetail.Master.Name) ? null : masterDetail.Master.Name;
                questionnaire.Title       = string.IsNullOrWhiteSpace(masterDetail.Master.Title) ? null : masterDetail.Master.Title;
                questionnaire.Status      = string.IsNullOrWhiteSpace(masterDetail.Master.Status) ? null : EnumUtility.ParseLiteral <PublicationStatus>(masterDetail.Master.Status);
                questionnaire.Date        = string.IsNullOrWhiteSpace(masterDetail.Master.Date) ? null : masterDetail.Master.Date;
                questionnaire.Publisher   = string.IsNullOrWhiteSpace(masterDetail.Master.Publisher) ? null : masterDetail.Master.Publisher;
                questionnaire.Description = string.IsNullOrWhiteSpace(masterDetail.Master.Description) ? null : new Markdown(masterDetail.Master.Description);
                questionnaire.Purpose     = string.IsNullOrWhiteSpace(masterDetail.Master.Purpose) ? null : new Markdown(masterDetail.Master.Purpose);
                questionnaire.Contact     = string.IsNullOrWhiteSpace(masterDetail.Master.Contact) ? null : new List <ContactDetail> {
                    new ContactDetail {
                        Telecom = new List <ContactPoint> {
                            new ContactPoint {
                                System = ContactPoint.ContactPointSystem.Url, Value = masterDetail.Master.Contact
                            }
                        }
                    }
                };

                if (!string.IsNullOrWhiteSpace(masterDetail.Master.Language))
                {
                    questionnaire.Language = masterDetail.Master.Language;
                    // TODO: Vi trenger definere Visningsnavn for språket, eksempelvis: Norsk (bokmål), osv.
                    questionnaire.Meta.Tag.Add(new Coding("urn:ietf:bcp:47", questionnaire.Language));
                }

                IList <string> linkIds           = new List <string>();
                Questionnaire.ItemComponent item = null;
                for (int i = 0; i < masterDetail.Details.Length; i++)
                {
                    QuestionnaireItem questionnaireItem = masterDetail.Details[i];

                    if (linkIds.IndexOf(questionnaireItem.LinkId) > 0)
                    {
                        throw new DuplicateLinkIdException(questionnaireItem.LinkId);
                    }

                    _logger.LogDebug($"Questionnaire Item: {questionnaireItem.LinkId} - {questionnaireItem.Type} - {questionnaireItem.Text}");

                    int level = questionnaireItem.LinkId.Split('.').Length - 1;
                    if (level > 0)
                    {
                        i = DiveV1(i, level, item.Item, masterDetail.Details);
                    }
                    else
                    {
                        item = CreateItemComponentV1(questionnaireItem);
                        questionnaire.Item.Add(item);
                    }
                }

                questionnaires.Add(questionnaire);
            }

            return(questionnaires);
        }
Example #14
0
        private Questionnaire.ItemComponent CreateItemComponentV2(QuestionnaireItem2 item)
        {
            Questionnaire.QuestionnaireItemType?itemType = EnumUtility.ParseLiteral <Questionnaire.QuestionnaireItemType>(item.Type);
            if (!itemType.HasValue)
            {
                throw new Exception($"QuestionnaireItemType at question with linkId: {item.LinkId} is not conforming to any valid literals. QuestionnaireItemType: {item.Type}");
            }

            Questionnaire.ItemComponent itemComponent = new Questionnaire.ItemComponent
            {
                Type = itemType,
            };

            itemComponent.LinkId = string.IsNullOrWhiteSpace(item.LinkId) ? null : item.LinkId;
            itemComponent.Prefix = string.IsNullOrWhiteSpace(item.Prefix) ? null : item.Prefix;
            itemComponent.Text   = string.IsNullOrWhiteSpace(item.Text) ? null : item.Text;
            if (!string.IsNullOrWhiteSpace(item.EnableWhen))
            {
                itemComponent.EnableWhen = ParseEnableWhen(item.EnableWhen).ToList();
                // TODO: Defaults to 'any' in the first iteration of "migrate to R4".
                itemComponent.EnableBehavior = Questionnaire.EnableWhenBehavior.Any;
            }
            if (itemType != Questionnaire.QuestionnaireItemType.Group && itemType != Questionnaire.QuestionnaireItemType.Display)
            {
                itemComponent.Required = item.Required.HasValue ? item.Required : null;
                itemComponent.ReadOnly = item.ReadOnly;
                itemComponent.Initial  = string.IsNullOrEmpty(item.Initial)
                    ? null
                    : new List <Questionnaire.InitialComponent> {
                    new Questionnaire.InitialComponent {
                        Value = GetElement(itemType.Value, item.Initial)
                    }
                };
                itemComponent.MaxLength = item.MaxLength.HasValue ? item.MaxLength : null;
            }

            if (itemType != Questionnaire.QuestionnaireItemType.Display)
            {
                itemComponent.Repeats = item.Repeats;
            }

            if (!string.IsNullOrWhiteSpace(item.ValidationText))
            {
                itemComponent.SetStringExtension(Constants.ValidationTextUri, item.ValidationText);
            }

            if (!string.IsNullOrWhiteSpace(item.Options) && item.Options.IndexOf('#') == 0)
            {
                itemComponent.AnswerValueSetElement = new Canonical($"#{item.Options.Substring(1)}");
            }
            else if (!string.IsNullOrWhiteSpace(item.Options) &&
                     (item.Options.IndexOf("http://") == 0 || item.Options.IndexOf("https://") == 0))
            {
                itemComponent.AnswerValueSetElement = new Canonical(item.Options);
            }

            if (!string.IsNullOrWhiteSpace(item.EntryFormat))
            {
                itemComponent.SetStringExtension(Constants.EntryFormatUri, item.EntryFormat);
            }

            if (item.MaxValueInteger.HasValue)
            {
                itemComponent.SetIntegerExtension(Constants.MaxValueUri, item.MaxValueInteger.Value);
            }
            if (item.MinValueInteger.HasValue)
            {
                itemComponent.SetIntegerExtension(Constants.MinValueUri, item.MinValueInteger.Value);
            }

            if (item.MaxValueDate.HasValue)
            {
                itemComponent.SetExtension(Constants.MaxValueUri, new FhirDateTime(new DateTimeOffset(item.MaxValueDate.Value.ToUniversalTime())));
            }
            if (item.MinValueDate.HasValue)
            {
                itemComponent.SetExtension(Constants.MinValueUri, new FhirDateTime(new DateTimeOffset(item.MinValueDate.Value.ToUniversalTime())));
            }

            if (item.MinLength.HasValue)
            {
                itemComponent.SetIntegerExtension(Constants.MinLenghtUri, item.MinLength.Value);
            }

            if (item.MaxDecimalPlaces.HasValue)
            {
                itemComponent.SetIntegerExtension(Constants.MaxDecimalPlacesUri, item.MaxDecimalPlaces.Value);
            }

            if (!string.IsNullOrWhiteSpace(item.RepeatsText))
            {
                itemComponent.SetStringExtension(Constants.RepeatsTextUri, item.RepeatsText);
            }

            if (!string.IsNullOrWhiteSpace(item.ItemControl))
            {
                CodeableConcept codeableConcept = new CodeableConcept
                {
                    Coding = new List <Coding> {
                        new Coding
                        {
                            System = Constants.ItemControlSystem,
                            Code   = item.ItemControl
                        }
                    }
                };

                itemComponent.SetExtension(Constants.ItemControlUri, codeableConcept);
            }

            if (item.MaxOccurs.HasValue)
            {
                itemComponent.SetIntegerExtension(Constants.MaxOccursUri, item.MaxOccurs.Value);
            }
            if (item.MinOccurs.HasValue)
            {
                itemComponent.SetIntegerExtension(Constants.MinOccursUri, item.MinOccurs.Value);
            }

            if (!string.IsNullOrWhiteSpace(item.Regex))
            {
                itemComponent.SetStringExtension(Constants.RegexUri, item.Regex);
            }

            if (!string.IsNullOrWhiteSpace(item.Markdown))
            {
                if (itemComponent.Text == null)
                {
                    throw new MissingRequirementException($"Question with linkId: {item.LinkId}. The 'Text' attribute is required when setting the 'Markdown' extension so that form fillers which do not support the 'Markdown' extension still can display informative text to the user.");
                }
                itemComponent.TextElement.SetExtension(Constants.RenderingMarkdownUri, new Markdown(item.Markdown));
            }
            if (!string.IsNullOrWhiteSpace(item.Unit))
            {
                Coding unitCoding = ParseCoding(item.Unit);
                itemComponent.SetExtension(Constants.QuestionnaireUnitUri, unitCoding);
            }

            if (!string.IsNullOrWhiteSpace(item.Code))
            {
                itemComponent.Code = ParseArrayOfCoding(item.Code);
            }

            if (!string.IsNullOrWhiteSpace(item.Option))
            {
                List <Element> options = ParseArrayOfElement(item.Option);
                foreach (DataType element in options)
                {
                    if (element is ResourceReference)
                    {
                        itemComponent.AddExtension(Constants.OptionReferenceUri, element);
                    }
                    else
                    {
                        itemComponent.AnswerOption.Add(new Questionnaire.AnswerOptionComponent {
                            Value = element
                        });
                    }
                }
            }

            if (!string.IsNullOrWhiteSpace(item.FhirPathExpression))
            {
                itemComponent.SetStringExtension(Constants.FhirPathUri, item.FhirPathExpression);
            }

            if (item.Hidden)
            {
                itemComponent.SetBoolExtension(Constants.QuestionnaireItemHidden, item.Hidden);
            }

            if (item.AttachmentMaxSize.HasValue && itemComponent.Type == Questionnaire.QuestionnaireItemType.Attachment)
            {
                itemComponent.SetExtension(Constants.QuestionnaireAttachmentMaxSize, new FhirDecimal(item.AttachmentMaxSize));
            }

            if (!string.IsNullOrWhiteSpace(item.CalculatedExpression))
            {
                itemComponent.SetStringExtension(Constants.CalculatedExpressionUri, item.CalculatedExpression);
            }

            if (!string.IsNullOrWhiteSpace(item.GuidanceAction))
            {
                itemComponent.SetStringExtension(Constants.GuidanceActionUri, item.GuidanceAction.Trim());
            }

            if (!string.IsNullOrWhiteSpace(item.GuidanceParameter))
            {
                itemComponent.SetStringExtension(Constants.GuidanceParameterUri, $"hn_frontend_{item.GuidanceParameter.Trim()}");
            }

            if (!string.IsNullOrWhiteSpace(item.FhirPathValidation))
            {
                itemComponent.SetStringExtension(Constants.FhirPathValidationUri, item.FhirPathValidation);
            }

            if (!string.IsNullOrWhiteSpace(item.FhirPathMaxValue))
            {
                itemComponent.SetStringExtension(Constants.SdfMaxValueUri, item.FhirPathMaxValue);
            }

            if (!string.IsNullOrWhiteSpace(item.FhirPathMinValue))
            {
                itemComponent.SetStringExtension(Constants.SdfMinValueUri, item.FhirPathMinValue);
            }

            if (!string.IsNullOrWhiteSpace(item.EnableBehavior))
            {
                itemComponent.EnableBehavior = EnumUtility.ParseLiteral <Questionnaire.EnableWhenBehavior>(item.EnableBehavior.ToLowerInvariant());
            }

            return(itemComponent);
        }