/// <summary> /// Given a observation POCO, maps the data to an Observation Resource. /// </summary> /// <param name="observation"></param> /// <returns></returns> public static Observation MapModel(POCO.Observation observation) { if (observation == null) { throw new ArgumentNullException("observation"); } var resource = new Observation(); resource.Id = observation.ObservationId.ToString("D"); // Observation Status switch (observation.Status) { case ObsStatus.amended: resource.Status = ObservationStatus.Amended; break; case ObsStatus.cancelled: resource.Status = ObservationStatus.Cancelled; break; case ObsStatus.entered_in_error: resource.Status = ObservationStatus.EnteredInError; break; case ObsStatus.final: resource.Status = ObservationStatus.Final; break; case ObsStatus.preliminary: resource.Status = ObservationStatus.Preliminary; break; case ObsStatus.registered: resource.Status = ObservationStatus.Registered; break; case ObsStatus.unknown: resource.Status = ObservationStatus.Unknown; break; default: resource.Status = ObservationStatus.EnteredInError; break; } // Observation Category if (observation.CategoryCode[0] != string.Empty || observation.CategoryDisplay[0] != string.Empty || observation.CategorySystem[0] != string.Empty || observation.CategoryText != null) { CodeableConcept observationCategory = new CodeableConcept(); List <Coding> observationCodings = new List <Coding>(); if (observation.CategoryCode[0] != string.Empty || observation.CategoryDisplay[0] != string.Empty || observation.CategorySystem[0] != string.Empty) { for (int i = 0; i < observation.CategoryCode.Count; i++) { Coding observationCoding = new Coding() { System = observation.CategorySystem[i], Display = observation.CategoryDisplay[i], Code = observation.CategoryCode[i] }; observationCodings.Add(observationCoding); } observationCategory.Coding = observationCodings; } observationCategory.Text = observation.CategoryText; resource.Category = observationCategory; } // Observation Code if (observation.CodeCode[0] != string.Empty || observation.CodeDisplay[0] != string.Empty || observation.CodeSystem[0] != string.Empty || observation.CodeText != null) { CodeableConcept observationCode = new CodeableConcept(); List <Coding> observationCodings = new List <Coding>(); if (observation.CodeCode[0] != string.Empty || observation.CodeDisplay[0] != string.Empty || observation.CodeSystem[0] != string.Empty) { for (int i = 0; i < observation.CodeCode.Count; i++) { Coding observationCoding = new Coding() { System = observation.CodeSystem[i], Display = observation.CodeDisplay[i], Code = observation.CodeCode[i] }; observationCodings.Add(observationCoding); } observationCode.Coding = observationCodings; } observationCode.Text = observation.CategoryText; resource.Code = observationCode; } // Observation references to other resources if (observation.PatientReference != null) { resource.Subject = new ResourceReference(); resource.Subject.Reference = observation.PatientReference; } if (observation.DeviceReference != null) { resource.Device = new ResourceReference(); resource.Device.Reference = observation.DeviceReference; } if (observation.PerformerReferences[0] != string.Empty) { foreach (var reference in observation.PerformerReferences) { ResourceReference performerReference = new ResourceReference(); performerReference.Reference = reference; resource.Performer.Add(performerReference); } } // Observation Effective times // The choice of Type is DateTime. if (observation.EffectiveDateTime != null) { FhirDateTime dateTime = new FhirDateTime(observation.EffectiveDateTime); resource.Effective = dateTime; } // The choice of Type is Period. else { Period period = new Period { Start = observation.EffectivePeriodStart.ToString(), End = observation.EffectivePeriodEnd.ToString() }; resource.Effective = period; } resource.Issued = observation.Issued; // Observation Comments resource.Comments = observation.Comments; // Site of Body where Observation was made if (observation.BodySiteCode != null || observation.BodySiteDisplay != null || observation.BodySiteSystem != null || observation.BodySiteText != null) { CodeableConcept observationBodySite = new CodeableConcept(); List <Coding> observationCodings = new List <Coding>(); if (observation.BodySiteCode != null || observation.BodySiteDisplay != null || observation.BodySiteSystem != null) { Coding observationCoding = new Coding() { System = observation.BodySiteSystem, Display = observation.BodySiteDisplay, Code = observation.BodySiteCode }; observationCodings.Add(observationCoding); observationBodySite.Coding = observationCodings; } observationBodySite.Text = observation.BodySiteText; resource.BodySite = observationBodySite; } // Observation Interpretation if (observation.InterpretationCode != null || observation.InterpretationDisplay != null || observation.InterpretationSystem != null || observation.InterpretationText != null) { CodeableConcept observationInterpretation = new CodeableConcept(); List <Coding> observationCodings = new List <Coding>(); if (observation.InterpretationCode != null || observation.InterpretationDisplay != null || observation.InterpretationSystem != null) { Coding observationCoding = new Coding() { System = observation.InterpretationSystem, Display = observation.InterpretationDisplay, Code = observation.InterpretationCode }; observationCodings.Add(observationCoding); observationInterpretation.Coding = observationCodings; } observationInterpretation.Text = observation.InterpretationText; resource.Interpretation = observationInterpretation; } // Observation Values // Values are componentised if (observation.ComponentCodeCode[0] != string.Empty || observation.ComponentCodeText != null) { for (int i = 0; i < observation.ComponentCodeCode.Count; i++) { ComponentComponent component = new ComponentComponent(); CodeableConcept concept = new CodeableConcept(); Coding coding = new Coding(); coding.Code = observation.ComponentCodeCode[i]; coding.Display = observation.ComponentCodeDisplay[i]; coding.System = observation.ComponentCodeSystem[i]; concept.Coding.Add(coding); concept.Text = observation.ComponentCodeText; component.Code = concept; resource.Component.Add(component); // value is of Type Quantity if (observation.ValueQuantityValue != null) { Quantity quantity = new Quantity(); quantity.Code = observation.ValueQuantityCode[i]; quantity.System = observation.ValueQuantitySystem[i]; quantity.Unit = observation.ValueQuantityUnit[i]; quantity.Value = observation.ValueQuantityValue[i]; resource.Component[i].Value = quantity; } // value is of Type CodeableConcept else if (observation.ValueCode[0] != string.Empty) { concept = new CodeableConcept(); coding = new Coding(); coding.Code = observation.ValueCode[i]; coding.Display = observation.ValueDisplay[i]; coding.System = observation.ValueSystem[i]; concept.Coding.Add(coding); concept.Text = observation.ValueText[i]; resource.Component[i].Value = concept; } // value is of Type String else if (observation.ValueString[0] != string.Empty) { FhirString fhirString = new FhirString(); fhirString.Value = observation.ValueString[i]; resource.Component[i].Value = fhirString; } // value is of Type SampledData else if (observation.ValueSampledDataOriginValue != null) { SampledData sampleData = new SampledData(); SimpleQuantity quantity = new SimpleQuantity(); quantity.Code = observation.ValueSampledDataOriginCode[i]; quantity.System = observation.ValueSampledDataOriginSystem[i]; quantity.Unit = observation.ValueSampledDataOriginUnit[i]; quantity.Value = observation.ValueSampledDataOriginValue[i]; sampleData.Origin = quantity; sampleData.Data = observation.ValueSampledDataData[i]; sampleData.Dimensions = observation.ValueSampledDataDimensions[i]; sampleData.Period = observation.ValueSampledDataPeriod[i]; resource.Component[i].Value = sampleData; } // value is of Type Period else if (observation.ValuePeriodStart != null) { Period period = new Period(); period.Start = observation.ValuePeriodStart[i].ToString(); period.End = observation.ValuePeriodEnd[i].ToString(); resource.Component[i].Value = period; } // No value provided else { resource.Component[i].Value = null; } } } //There is only one "set" of values else { // value is of Type Quantity if (observation.ValueQuantityValue != null) { Quantity quantity = new Quantity(); quantity.Code = observation.ValueQuantityCode[0]; quantity.System = observation.ValueQuantitySystem[0]; quantity.Unit = observation.ValueQuantityUnit[0]; quantity.Value = observation.ValueQuantityValue[0]; resource.Value = quantity; } // value is of Type CodeableConcept else if (observation.ValueCode[0] != string.Empty) { CodeableConcept concept = new CodeableConcept(); Coding coding = new Coding(); coding.Code = observation.ValueCode[0]; coding.Display = observation.ValueDisplay[0]; coding.System = observation.ValueSystem[0]; concept.Coding.Add(coding); concept.Text = observation.ValueText[0]; resource.Value = concept; } // value is of Type String else if (observation.ValueString[0] != string.Empty) { FhirString fhirString = new FhirString(); fhirString.Value = observation.ValueString[0]; resource.Value = fhirString; } // value is of Type SampledData else if (observation.ValueSampledDataOriginValue != null) { SampledData sampleData = new SampledData(); SimpleQuantity quantity = new SimpleQuantity(); quantity.Code = observation.ValueSampledDataOriginCode[0]; quantity.System = observation.ValueSampledDataOriginSystem[0]; quantity.Unit = observation.ValueSampledDataOriginUnit[0]; quantity.Value = observation.ValueSampledDataOriginValue[0]; sampleData.Origin = quantity; sampleData.Data = observation.ValueSampledDataData[0]; sampleData.Dimensions = observation.ValueSampledDataDimensions[0]; sampleData.Period = observation.ValueSampledDataPeriod[0]; resource.Value = sampleData; } // value is of Type Period else if (observation.ValuePeriodStart != null) { Period period = new Period(); period.Start = observation.ValuePeriodStart[0].ToString(); period.End = observation.ValuePeriodEnd[0].ToString(); resource.Value = period; } // No value provided. else { resource.Value = null; } } return(resource); }
private List <(string ResourceType, SearchParameterInfo SearchParameter)> ValidateAndGetFlattenedList() { var issues = new List <OperationOutcomeIssue>(); Bundle bundle = null; if (!string.IsNullOrWhiteSpace(_unsupportedParamsEmbeddedResourceName)) { using Stream stream = _assembly.GetManifestResourceStream(_unsupportedParamsEmbeddedResourceName); using TextReader reader = new StreamReader(stream); _unsupportedParams = JsonConvert.DeserializeObject <UnsupportedSearchParameters>(reader.ReadToEnd()); } using (Stream stream = _assembly.GetManifestResourceStream(_embeddedResourceName)) { using TextReader reader = new StreamReader(stream); using JsonReader jsonReader = new JsonTextReader(reader); try { // The parser does some basic validation such as making sure entry is not null, enum has the right value, and etc. bundle = _fhirJsonParser.Parse <Bundle>(jsonReader); } catch (FormatException ex) { AddIssue(ex.Message); } } EnsureNoIssues(); // Do the first pass to make sure all resources are SearchParameter. for (int entryIndex = 0; entryIndex < bundle.Entry.Count; entryIndex++) { // Make sure resources are not null and they are SearchParameter. EntryComponent entry = bundle.Entry[entryIndex]; var searchParameter = entry.Resource as SearchParameter; if (searchParameter == null) { AddIssue(Core.Resources.SearchParameterDefinitionInvalidResource, entryIndex); continue; } try { _uriDictionary.Add(new Uri(searchParameter.Url), CreateSearchParameterInfo(searchParameter)); } catch (FormatException) { AddIssue(Core.Resources.SearchParameterDefinitionInvalidDefinitionUri, entryIndex); continue; } catch (ArgumentException) { AddIssue(Core.Resources.SearchParameterDefinitionDuplicatedEntry, searchParameter.Url); continue; } } EnsureNoIssues(); var validatedSearchParameters = new List <(string ResourceType, SearchParameterInfo SearchParameter)> { // _type is currently missing from the search params definition bundle, so we inject it in here. (ResourceType.Resource.ToString(), new SearchParameterInfo(SearchParameterNames.ResourceType, SearchParamType.Token.ToString(), SearchParameterNames.ResourceTypeUri, null, "Resource.type().name", null)), }; // Do the second pass to make sure the definition is valid. for (int entryIndex = 0; entryIndex < bundle.Entry.Count; entryIndex++) { var searchParameter = (SearchParameter)bundle.Entry[entryIndex].Resource; // If this is a composite search parameter, then make sure components are defined. if (searchParameter.Type == SearchParamType.Composite) { if (searchParameter.Component?.Count == 0) { AddIssue(Core.Resources.SearchParameterDefinitionInvalidComponent, searchParameter.Url); continue; } for (int componentIndex = 0; componentIndex < searchParameter.Component.Count; componentIndex++) { ComponentComponent component = searchParameter.Component[componentIndex]; if (component.GetComponentDefinitionUri() == null || !_uriDictionary.TryGetValue(component.GetComponentDefinitionUri(), out SearchParameterInfo componentSearchParameter)) { AddIssue( Core.Resources.SearchParameterDefinitionInvalidComponentReference, searchParameter.Url, componentIndex); continue; } if (componentSearchParameter.Type == SearchParamType.Composite.ToValueSet()) { AddIssue( Core.Resources.SearchParameterDefinitionComponentReferenceCannotBeComposite, searchParameter.Url, componentIndex); continue; } if (string.IsNullOrWhiteSpace(component.Expression)) { AddIssue( Core.Resources.SearchParameterDefinitionInvalidComponentExpression, searchParameter.Url, componentIndex); continue; } } } // Make sure the base is defined. if (searchParameter.BaseElement?.Count == 0) { AddIssue(Core.Resources.SearchParameterDefinitionBaseNotDefined, searchParameter.Url); continue; } for (int baseElementIndex = 0; baseElementIndex < searchParameter.BaseElement.Count; baseElementIndex++) { Code <ResourceType> code = searchParameter.BaseElement[baseElementIndex]; string baseResourceType = code.Value.Value.ToString(); // Make sure the expression is not empty unless they are known to have empty expression. // These are special search parameters that searches across all properties and needs to be handled specially. if (ShouldExcludeEntry(baseResourceType, searchParameter.Name)) { continue; } else { if (string.IsNullOrWhiteSpace(searchParameter.Expression)) { AddIssue(Core.Resources.SearchParameterDefinitionInvalidExpression, searchParameter.Url); continue; } } validatedSearchParameters.Add((baseResourceType, CreateSearchParameterInfo(searchParameter))); } } EnsureNoIssues(); return(validatedSearchParameters); void AddIssue(string format, params object[] args) { issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Fatal, OperationOutcomeConstants.IssueType.Invalid, string.Format(CultureInfo.InvariantCulture, format, args))); } void EnsureNoIssues() { if (issues.Count != 0) { throw new InvalidDefinitionException( Core.Resources.SearchParameterDefinitionContainsInvalidEntry, issues.ToArray()); } } }
public Expression Parse( SearchParameter searchParameter, SearchModifierCode?modifier, string value) { EnsureArg.IsNotNull(searchParameter, nameof(searchParameter)); Debug.Assert( modifier == null || Enum.IsDefined(typeof(SearchModifierCode), modifier.Value), "Invalid modifier."); EnsureArg.IsNotNullOrWhiteSpace(value, nameof(value)); Expression outputExpression; if (modifier == SearchModifierCode.Missing) { // We have to handle :missing modifier specially because if :missing modifier is specified, // then the value is a boolean string indicating whether the parameter is missing or not instead of // the search value type associated with the search parameter. if (!bool.TryParse(value, out bool isMissing)) { // An invalid value was specified. throw new InvalidSearchOperationException(Core.Resources.InvalidValueTypeForMissingModifier); } return(Expression.MissingSearchParameter(searchParameter, isMissing)); } if (modifier == SearchModifierCode.Text) { // We have to handle :text modifier specially because if :text modifier is supplied for token search param, // then we want to search the display text using the specified text, and therefore // we don't want to actually parse the specified text into token. if (searchParameter.Type != SearchParamType.Token) { throw new InvalidSearchOperationException( string.Format(CultureInfo.InvariantCulture, Core.Resources.ModifierNotSupported, modifier, searchParameter.Name)); } outputExpression = Expression.Contains(FieldName.TokenText, null, value, true); } else { // Build the expression for based on the search value. if (searchParameter.Type == SearchParamType.Composite) { if (modifier != null) { throw new InvalidSearchOperationException( string.Format(CultureInfo.InvariantCulture, Core.Resources.ModifierNotSupported, modifier, searchParameter.Name)); } IReadOnlyList <string> compositeValueParts = value.SplitByCompositeSeparator(); if (compositeValueParts.Count > searchParameter.Component.Count) { throw new InvalidSearchOperationException( string.Format(CultureInfo.InvariantCulture, Core.Resources.NumberOfCompositeComponentsExceeded, searchParameter.Name)); } var compositeExpressions = new Expression[compositeValueParts.Count]; for (int i = 0; i < compositeValueParts.Count; i++) { ComponentComponent component = searchParameter.Component[i]; // Find the corresponding search parameter info. SearchParameter componentSearchParameter = _searchParameterDefinitionManager.GetSearchParameter(component.Definition.Url); string componentValue = compositeValueParts[i]; compositeExpressions[i] = Build( componentSearchParameter, modifier: null, componentIndex: i, value: componentValue); } outputExpression = Expression.And(compositeExpressions); } else { outputExpression = Build( searchParameter, modifier, componentIndex: null, value: value); } } return(Expression.SearchParameter(searchParameter, outputExpression)); }