def get_date_time_value(primitive: message.Message) -> datetime.datetime: """Returns a Python-native datetime.datetime from the wrapped primitive.""" tzinfo = timezone_info_for_timezone( cast(str, proto_utils.get_value_at_field(primitive, 'timezone'))) delta = datetime.timedelta(microseconds=cast( int, proto_utils.get_value_at_field(primitive, 'value_us'))) return (UNIX_EPOCH + delta).astimezone(tzinfo)
def reference_to_string(reference: message.Message) -> str: """Returns a reference URI for a typed reference message.""" _validate_reference(reference) # Early-exit if URI or fragment is set if proto_utils.field_is_set(reference, 'uri'): uri = proto_utils.get_value_at_field(reference, 'uri') return uri.value elif proto_utils.field_is_set(reference, 'fragment'): fragment = proto_utils.get_value_at_field(reference, 'fragment') return f'#{fragment.value}' set_oneof = reference.WhichOneof('reference') if set_oneof is None: raise ValueError( f'Reference is not set on: {reference.DESCRIPTOR.name}.') # Convert to CamelCase prefix = path_utils.snake_case_to_camel_case(set_oneof, upper=True) # Cull trailing 'Id' if prefix.endswith('Id'): prefix = prefix[:-2] reference_id = proto_utils.get_value_at_field(reference, set_oneof) reference_string = f'{prefix}/{reference_id.value}' if proto_utils.field_is_set(reference_id, 'history'): reference_string += f'/_history/{reference_id.history.value}' return reference_string
def _validate_period(period: message.Message, base_name: str): """Validates that a timelike period has a valid start and end value.""" if not (proto_utils.field_is_set(period, 'start') and proto_utils.field_is_set(period, 'end')): return # Early exit; either start or end field is not present # Check whether start time is greater than end time. Note that, if it is, # that's not necessarily invalid, since the precisions can be different. So we # need to compare the end time at the upper bound of the end element. # # Example: If the start time is "Tuesday at noon", and the end time is "some # time Tuesday", this is valid even though the timestamp used for "some time # Tuesday" is Tuesday 00:00, since the precision for the start is higher than # the end. # # Also note the GetUpperBoundFromTimelikeElement is always greater than the # time itself by exactly one time unit, and hence start needs to be strictly # less than the upper bound of end, so as not to allow ranges like [Tuesday, # Monday] to be valid. start: message.Message = proto_utils.get_value_at_field(period, 'start') end: message.Message = proto_utils.get_value_at_field(period, 'end') end_precision = proto_utils.get_value_at_field(end, 'precision') start_dt_value = _primitive_time_utils.get_date_time_value(start) end_upper_bound = _primitive_time_utils.get_upper_bound( end, _primitive_time_utils.DateTimePrecision(end_precision)) if start_dt_value >= end_upper_bound: raise fhir_errors.InvalidFhirError( f'`{base_name}` start time is later than end time.')
def get_system_for_code(code: message.Message) -> str: """Returns the code system associated with the provided Code.""" system_field = code.DESCRIPTOR.fields_by_name.get('system') if system_field is not None: return proto_utils.get_value_at_field(code, system_field) fixed_coding_system = annotation_utils.get_fixed_coding_system(code) if fixed_coding_system is not None: # The entire profiled coding can only be from a single system. Use that. return fixed_coding_system # There is no single system for the whole coding. Look for the coding system # annotation on the enum. enum_field = code.DESCRIPTOR.fields_by_name.get('value') if (enum_field is None or enum_field.type != descriptor.FieldDescriptor.TYPE_ENUM): raise fhir_errors.InvalidFhirError( f'Invalid profiled Coding: {code.DESCRIPTOR.full_name}; missing system ' 'information on string code.') enum_value = proto_utils.get_value_at_field(code, enum_field) enum_value_descriptor = enum_field.enum_type.values_by_number[enum_value] if not annotation_utils.has_source_code_system(enum_value_descriptor): raise fhir_errors.InvalidFhirError( f'Invalid profiled Coding: {code.DESCRIPTOR.full_name}; missing system ' 'information on enum code') return cast(str, annotation_utils.get_source_code_system(enum_value_descriptor))
def _nonnull_string_value(self) -> str: precision: int = proto_utils.get_value_at_field( self.wrapped, 'precision') f = _FORMAT_FUNCS.get(precision) if f is None: raise fhir_errors.InvalidFhirError( f'No format string for precision: {precision!r}.') # The wrapped value represents the time (based on a 24-hour clock) in micro- # seconds since 00:00:00.000000. In order to convert this to a datetime.time # object for formatting, we need to use a datetime.timedelta to compute the # time "offset" represented by self.wrapped. # # A timedelta object can only be added to a datetime. Note that the base # datetime created by datetime.combine() has no associated tzinfo (e.g. it # is "naive"), and therefore concepts like DST, timezones, etc. will not # affect this calculation. base_time = datetime.datetime.combine(_primitive_time_utils.UNIX_EPOCH, datetime.time()) value_us: int = proto_utils.get_value_at_field(self.wrapped, 'value_us') delta = datetime.timedelta(microseconds=value_us) if delta.days > 0: raise fhir_errors.InvalidFhirError( f'Time value of {value_us!r}us is >= 24 hours.') time_value = (base_time + delta).time() return f(time_value)
def _nonnull_string_value(self) -> str: separator_extensions: List[Any] = extensions.get_repeated_from_extensions( proto_utils.get_value_at_field(self.wrapped, 'extension'), self._separator_stride_cls) value = proto_utils.get_value_at_field(self.wrapped, 'value') encoded = base64.b64encode(value).decode(encoding='ascii') if separator_extensions: encoded = _separate_string(encoded, separator_extensions[0].stride.value, separator_extensions[0].separator.value) return encoded
def _nonnull_string_value(self) -> str: timezone: str = proto_utils.get_value_at_field(self.wrapped, 'timezone') if not timezone: raise fhir_errors.InvalidFhirError('DateTime missing timezone.') precision: int = proto_utils.get_value_at_field(self.wrapped, 'precision') f = _FORMAT_FUNCS.get(precision) if f is None: raise fhir_errors.InvalidFhirError('Invalid Precision on DateTime') dt_str = f(_primitive_time_utils.get_date_time_value(self.wrapped)) return _primitive_time_utils.restore_utc_timezone(dt_str, timezone)
def split_if_relative_reference(reference: message.Message): """Splits relative references into their components. For example, a reference with the uri set to "Patient/ABCD" will be changed into a reference with the patientId set to "ABCD". Args: reference: The FHIR reference to potentially split. """ _validate_reference(reference) uri_field = reference.DESCRIPTOR.fields_by_name.get('uri') if not proto_utils.field_is_set(reference, uri_field): return # No URI to split uri = proto_utils.get_value_at_field(reference, uri_field) # We're permissive about various full URL schemes. If we're able to find a # match, the URI is valid as-is. url_match = re.fullmatch(_URL_REFERENCE_PATTERN, uri.value) if url_match is not None: return # URI is valid internal_match = re.fullmatch(_INTERNAL_REFERENCE_PATTERN, uri.value) if internal_match is not None: # Note that we make the reference_id off of the reference before adding it, # since adding the reference_id would destroy the uri field, as they are # both in the same oneof. This allows us to copy fields from uri to # reference_id without making an extra copy. reference_id_field = get_reference_id_field_for_resource( reference, internal_match.group('resource_type')) reference_id = proto_utils.create_message_from_descriptor( reference_id_field.message_type) populate_typed_reference_id(reference_id, internal_match.group('resource_id'), internal_match.group('version')) proto_utils.copy_common_field(uri, reference_id, 'id') proto_utils.copy_common_field(uri, reference_id, 'extension') proto_utils.set_value_at_field(reference, reference_id_field, reference_id) return fragment_match = re.fullmatch(_FRAGMENT_REFERENCE_PATTERN, uri.value) if fragment_match is not None: # Note that we make the fragment off of the reference before adding it, # since adding the fragment would destroy the uri field, as they are both in # the same oneof. This allows us to copy fields from uri to fragment without # making an extra copy. fragment_field = reference.DESCRIPTOR.fields_by_name['fragment'] fragment = proto_utils.create_message_from_descriptor( fragment_field.message_type) value_field = fragment.DESCRIPTOR.fields_by_name['value'] proto_utils.set_value_at_field(fragment, value_field, uri.value[1:]) proto_utils.copy_common_field(uri, fragment, 'id') proto_utils.copy_common_field(uri, fragment, 'extension') proto_utils.set_value_at_field(reference, fragment_field, fragment) return raise ValueError(f'String {uri.value!r} cannot be parsed as a reference.')
def _print_choice_field(self, field_name: str, field: descriptor.FieldDescriptor, choice_container: message.Message) -> None: """Prints a FHIR choice field. This field is expected to have one valid oneof set. Args: field_name: The name of the field. field: The FieldDescriptor whose contents to print. choice_container: The value present at field, which should be a oneof with a single value set. """ if len(choice_container.DESCRIPTOR.oneofs) != 1: raise ValueError(f'Invalid value for choice field {field_name}: ' f'{choice_container}.') oneof_group = choice_container.DESCRIPTOR.oneofs[0] set_oneof_name = choice_container.WhichOneof(oneof_group.name) if set_oneof_name is None: raise ValueError('Oneof not set on choice type: ' f'{choice_container.DESCRIPTOR.full_name}.') value_field = choice_container.DESCRIPTOR.fields_by_name[ set_oneof_name] oneof_field_name = value_field.json_name oneof_field_name = oneof_field_name[0].upper() + oneof_field_name[1:] value = proto_utils.get_value_at_field(choice_container, value_field) if annotation_utils.is_primitive_type(value_field.message_type): self._print_primitive_field(field_name + oneof_field_name, value_field, value) else: self._print_message_field(field_name + oneof_field_name, value_field, value)
def test_valid_pairs(self): for primitive_data in self.primitive_data_list: file_name = primitive_data.name field_name = primitive_data.field_name field_type = primitive_data.field_type file_path = os.path.join(_PRIMITIVE_TESTS_PATH, file_name + '.prototxt') test_suite = testdata_utils.read_proto( file_path, self.primitive_test_suite_class) for pair in test_suite.valid_pairs: expected_proto = proto_utils.get_value_at_field( pair.proto, field_name) expected_json = pair.json_string actual_json = self.json_format.print_fhir_to_json_string( expected_proto) actual_proto = self.json_format.json_fhir_string_to_proto( actual_json, field_type, default_timezone='Australia/Sydney') self.assertEqual(expected_json, actual_json) self.assertProtoEqual(expected_proto, actual_proto)
def _nonnull_string_value(self) -> str: timezone: str = proto_utils.get_value_at_field(self.wrapped, 'timezone') if not timezone: raise fhir_errors.InvalidFhirError('Date missing timezone.') precision: int = proto_utils.get_value_at_field(self.wrapped, 'precision') f = _FORMAT_FUNCS.get(precision) if f is None: raise fhir_errors.InvalidFhirError( 'No format string for precision: ' f'{_primitive_time_utils.DateTimePrecision(precision)!r}.') tzinfo = _primitive_time_utils.timezone_info_for_timezone(timezone) delta = datetime.timedelta( microseconds=cast( int, proto_utils.get_value_at_field(self.wrapped, 'value_us'))) dt_value = (_primitive_time_utils.UNIX_EPOCH + delta).astimezone(tzinfo) return f(dt_value)
def testGetValueAtField_withRepeatedComposite_returnsList(self): """Test get_value_at_field with a repeated composite field.""" patient_names = [ datatypes_pb2.HumanName(text=datatypes_pb2.String(value="Foo")), datatypes_pb2.HumanName(text=datatypes_pb2.String(value="Bar")), datatypes_pb2.HumanName(text=datatypes_pb2.String(value="Bats")), ] patient = patient_pb2.Patient(name=patient_names) result = proto_utils.get_value_at_field(patient, "name") self.assertEqual(result, patient_names)
def _print_primitive_field(self, field_name: str, field: descriptor.FieldDescriptor, value: Any) -> None: """Prints the primitive field. Args: field_name: The name of the field. field: The FielDescriptor whose contents to print. value: The value present at field to print. """ if proto_utils.field_is_repeated(field): string_values = [] elements = [] extensions_found = False nonnull_values_found = False for primitive in value: wrapper = self.primitive_handler.primitive_wrapper_from_primitive( primitive) string_values.append(wrapper.json_value()) elements.append(wrapper.get_element()) nonnull_values_found = nonnull_values_found or wrapper.has_value( ) extensions_found = extensions_found or wrapper.has_element() # print string primitive representations if nonnull_values_found: self.generator.add_field(field_name) self._print_list(string_values, self.generator.push) # print Element representations if extensions_found: if nonnull_values_found: self.generator.push(',') self.generator.add_newline() self.generator.add_field(f'_{field_name}') self._print_list(elements, self._print) else: # Singular field # TODO: Detect ReferenceId using an annotation if (self.json_format == _FhirJsonFormat.ANALYTIC and field.message_type.name == 'ReferenceId'): str_value = proto_utils.get_value_at_field(value, 'value') self.generator.add_field(field_name, f'"{str_value}"') else: # Wrap and print primitive value and (optionally), its element wrapper = self.primitive_handler.primitive_wrapper_from_primitive( value) if wrapper.has_value(): self.generator.add_field(field_name, wrapper.json_value()) if wrapper.has_element( ) and self.json_format == _FhirJsonFormat.PURE: if wrapper.has_value(): self.generator.push(',') self.generator.add_newline() self.generator.add_field(f'_{field_name}') self._print(wrapper.get_element())
def testSplitIfRelativeReference_withUrlScheme_succeeds(self): ref = datatypes_pb2.Reference(uri=datatypes_pb2.String( value='http://acme.com/ehr/fhir/Practitioner/2323-33-4')) references.split_if_relative_reference(ref) uri_field = ref.DESCRIPTOR.fields_by_name['uri'] self.assertEqual( proto_utils.get_value_at_field(ref, uri_field), datatypes_pb2.String( value='http://acme.com/ehr/fhir/Practitioner/2323-33-4'))
def _get_populated_extension_value_field( extension: message.Message) -> descriptor.FieldDescriptor: """Return the field descriptor for the oneof field that was set.""" value = proto_utils.get_value_at_field(extension, 'value') field_name = value.WhichOneof('choice') if field_name is None: raise ValueError( f'No value set on extension: {extension.DESCRIPTOR.full_name}.') field = value.DESCRIPTOR.fields_by_name[field_name] _verify_field_is_proto_message_type(field) return field
def _nonnull_string_value(self) -> str: timezone: str = proto_utils.get_value_at_field(self.wrapped, 'timezone') if not timezone: raise fhir_errors.InvalidFhirError('Instant missing timezone.') precision: int = proto_utils.get_value_at_field( self.wrapped, 'precision') f = _FORMAT_FUNCS.get(precision) if f is None: raise fhir_errors.InvalidFhirError('Invalid precision on Instant.') value_us: int = proto_utils.get_value_at_field(self.wrapped, 'value_us') tzinfo = _primitive_time_utils.timezone_info_for_timezone(timezone) delta = datetime.timedelta(microseconds=value_us) datetime_value = (_primitive_time_utils.UNIX_EPOCH + delta).astimezone(tzinfo) datetime_str = f(datetime_value) return _primitive_time_utils.restore_utc_timezone( datetime_str, timezone)
def add_message_to_extension(msg: message.Message, extension: message.Message) -> None: """Adds the contents of msg to extension. Args: msg: A FHIR profile of Extension, whose contents should be added to the generic extension. extension: The generic Extension to populate. """ if not fhir_types.is_profile_of_extension(msg): raise ValueError(f'Message: {msg.DESCRIPTOR.full_name} is not a valid ' 'FHIR Extension profile.') if not fhir_types.is_extension(extension): raise ValueError( f'Extension: {extension.DESCRIPTOR.full_name} is not a ' 'valid FHIR Extension.') cast(Any, extension).url.value = annotation_utils.get_structure_definition_url( msg.DESCRIPTOR) # Copy over the id field if present if proto_utils.field_is_set(msg, 'id'): proto_utils.copy_common_field(msg, extension, 'id') # Copy the vlaue fields from message into the extension value_fields = [ field for field in msg.DESCRIPTOR.fields if field.name not in NON_VALUE_FIELDS ] if not value_fields: raise ValueError( f'Extension has no value fields: {msg.DESCRIPTOR.name}.') # Add fields to the extension. If there is a single value field, a simple # value assignment will suffice. Otherwise, we need to loop over all fields # and add them as child extensions if (len(value_fields) == 1 and not proto_utils.field_is_repeated(value_fields[0])): value_field = value_fields[0] _verify_field_is_proto_message_type(value_field) if proto_utils.field_is_set(msg, value_field): value = proto_utils.get_value_at_field(msg, value_field) _add_value_to_extension( value, extension, annotation_utils.is_choice_type_field(value_field)) else: # TODO: Invalid FHIR; throw an error here? pass else: # Add child extensions... _add_fields_to_extension(msg, extension)
def copy_coding(source: message.Message, target: message.Message): """Copies all fields from source to target "Coding" messages. Args: source: The FHIR coding instance to copy from. target: The FHIR coding instance to copy to. Raises: InvalidFhirError: In the event that source or target is not a type/profile of Coding. """ if not fhir_types.is_type_or_profile_of_coding(source.DESCRIPTOR): raise fhir_errors.InvalidFhirError( f'Source: {source.DESCRIPTOR.full_name} ' 'is not a type or profile of Coding.') if not fhir_types.is_type_or_profile_of_coding(target.DESCRIPTOR): raise fhir_errors.InvalidFhirError( f'Target: {target.DESCRIPTOR.full_name} ' 'is not a type or profile of Coding.') if proto_utils.are_same_message_type(source.DESCRIPTOR, target.DESCRIPTOR): target.CopyFrom(source) return # Copy fields present in both profiled and unprofiled codings. proto_utils.copy_common_field(source, target, 'id') proto_utils.copy_common_field(source, target, 'extension') proto_utils.copy_common_field(source, target, 'version') proto_utils.copy_common_field(source, target, 'display') proto_utils.copy_common_field(source, target, 'user_selected') # Copy the "code" field from source to target source_code = proto_utils.get_value_at_field(source, 'code') copy_code(source_code, proto_utils.set_in_parent_or_add(target, 'code')) target_system_field = target.DESCRIPTOR.fields_by_name.get('system') # TODO: This will fail if there is a target system field, # *and* a source system field, since in this case the source code will not # contain the system information, the containing Coding would. In general, # it's not quite right to get the system from Code, since unprofiled codes # don't contain system information. In practice, this isn't a problem, # because the only kind of profiled Codings we currently support are # Codings with typed Codes (which contain source information) but this is # not neccessary according to FHIR spec. if target_system_field is not None: source_system_str = get_system_for_code(source_code) target_system_uri = proto_utils.set_in_parent_or_add( target, target_system_field) proto_utils.set_value_at_field(target_system_uri, 'value', source_system_str)
def testSplitIfRelativeReference_withFragmentReference_succeeds(self): ref = datatypes_pb2.Reference(uri=datatypes_pb2.String(value='#org-1')) uri_field = ref.DESCRIPTOR.fields_by_name['uri'] fragment_field = ref.DESCRIPTOR.fields_by_name['fragment'] self.assertTrue(proto_utils.field_is_set(ref, uri_field)) self.assertFalse(proto_utils.field_is_set(ref, fragment_field)) references.split_if_relative_reference(ref) self.assertFalse(proto_utils.field_is_set(ref, uri_field)) self.assertTrue(proto_utils.field_is_set(ref, fragment_field)) self.assertEqual(proto_utils.get_value_at_field(ref, fragment_field), datatypes_pb2.String(value='org-1'))
def test_valid_pairs(self, file_name: str, field_name: str, field_type: Type[message.Message]): test_suite = self.get_test_suite(file_name) for pair in test_suite.valid_pairs: expected_proto = proto_utils.get_value_at_field( pair.proto, field_name) expected_json = pair.json_string actual_json = json_format.print_fhir_to_json_string(expected_proto) actual_proto = json_format.json_fhir_string_to_proto( actual_json, field_type, default_timezone='Australia/Sydney') self.assertEqual(expected_json, actual_json) self.assertProtoEqual(expected_proto, actual_proto)
def testSplitIfRelativeReference_withRelativeReference_succeeds(self): ref = datatypes_pb2.Reference( uri=datatypes_pb2.String(value='Practitioner/example'), display=datatypes_pb2.String(value='Dr Adam Careful')) uri_field = ref.DESCRIPTOR.fields_by_name['uri'] practitioner_id_field = ref.DESCRIPTOR.fields_by_name['practitioner_id'] self.assertTrue(proto_utils.field_is_set(ref, uri_field)) self.assertFalse(proto_utils.field_is_set(ref, practitioner_id_field)) references.split_if_relative_reference(ref) self.assertFalse(proto_utils.field_is_set(ref, uri_field)) self.assertTrue(proto_utils.field_is_set(ref, practitioner_id_field)) self.assertEqual( proto_utils.get_value_at_field(ref, practitioner_id_field), datatypes_pb2.ReferenceId(value='example'))
def copy_coding(source: message.Message, target: message.Message): """Copies all fields from source to target "Coding" messages. Args: source: The FHIR coding instance to copy from. target: The FHIR coding instance to copy to. Raises: InvalidFhirError: In the event that source or target is not a type/profile of Coding. """ if not fhir_types.is_type_or_profile_of_coding(source.DESCRIPTOR): raise fhir_errors.InvalidFhirError( f'Source: {source.DESCRIPTOR.full_name} ' 'is not a type or profile of Coding.') if not fhir_types.is_type_or_profile_of_coding(target.DESCRIPTOR): raise fhir_errors.InvalidFhirError( f'Target: {target.DESCRIPTOR.full_name} ' 'is not a type or profile of Coding.') if proto_utils.are_same_message_type(source.DESCRIPTOR, target.DESCRIPTOR): target.CopyFrom(source) return # Copy fields present in both profiled and unprofiled codings. proto_utils.copy_common_field(source, target, 'id') proto_utils.copy_common_field(source, target, 'extension') proto_utils.copy_common_field(source, target, 'version') proto_utils.copy_common_field(source, target, 'display') proto_utils.copy_common_field(source, target, 'user_selected') # Copy the "code" field from source to target source_code = proto_utils.get_value_at_field(source, 'code') copy_code(source_code, proto_utils.set_in_parent_or_add(target, 'code')) target_system_field = target.DESCRIPTOR.fields_by_name.get('system') if target_system_field is not None: source_system_str = get_system_for_code(source_code) target_system_uri = proto_utils.set_in_parent_or_add( target, target_system_field) proto_utils.set_value_at_field(target_system_uri, 'value', source_system_str)
def _add_value_to_extension(msg: message.Message, extension: message.Message, is_choice_type: bool): """Adds the fields from msg to a generic Extension. Attempts are first made to set the "value" field of the generic Extension based on the type of field set on message. If this fails, checks are made against the generic Code and Coding types, and finally we fall back to adding the message's fields as sub-extensions. Args: msg: The message whose values to add to extension. extension: The generic Extension to populate. is_choice_type: Whether or not the provided message represents a "choice" type. """ if is_choice_type: oneofs = msg.DESCRIPTOR.oneofs if not oneofs: raise fhir_errors.InvalidFhirError( f'Choice type is missing a oneof: {msg.DESCRIPTOR.full_name}') value_field_name = msg.WhichOneof(oneofs[0].name) if value_field_name is None: raise ValueError('Choice type has no value set: ' f'{msg.DESCRIPTOR.full_name}') value_field = msg.DESCRIPTOR.fields_by_name[value_field_name] _verify_field_is_proto_message_type(value_field) _add_value_to_extension( proto_utils.get_value_at_field(msg, value_field), extension, False) else: # Try to set the message directly as a datatype value on the extension. # E.g., put the message of type Boolean into the value.boolean field value_field_mapping = _get_value_field_mapping_for_extension(extension) value_field = value_field_mapping.get(msg.DESCRIPTOR.full_name) if value_field is not None: proto_utils.set_value_at_field( cast(Any, extension).value, value_field, msg) elif annotation_utils.has_fhir_valueset_url(msg): codes.copy_code(msg, cast(Any, extension).value.code) elif fhir_types.is_type_or_profile_of_coding(msg): codes.copy_coding(msg, cast(Any, extension).value.coding) else: # Fall back to adding individual fields as sub-extensions _add_fields_to_extension(msg, extension)
def get_fhir_extensions(msg: message.Message) -> List[message.Message]: """Returns a list of FHIR extensions from the provided FHIR primitive. Args: msg: The Message to extract FHIR extensions from. Returns: A list of Extension values found on the instance. Raises: ValueError: If the provided FHIR primitive doesn't have an extension attribute. """ extension_field = msg.DESCRIPTOR.fields_by_name.get('extension') if extension_field is None: raise ValueError( f'Message of type {msg.DESCRIPTOR.name} does not have ' 'an extension attribute.') return proto_utils.get_value_at_field(msg, extension_field)
def _print_reference(self, reference: message.Message): """Standardizes and prints the provided reference. Note that "standardization" in this case refers to un-typing the typed- reference prior to printing. Args: reference: The reference to print. """ set_oneof = reference.WhichOneof('reference') if set_oneof is None or set_oneof == 'uri': # Reference is already standard self._print_message(reference) else: new_reference = copy.copy(reference) # Setting the new URI field will overwrite the oneof new_uri = proto_utils.get_value_at_field(new_reference, 'uri') proto_utils.set_value_at_field( new_uri, 'value', references.reference_to_string(reference)) self._print_message(new_reference)
def get_code_as_string(code: message.Message) -> str: """Returns the string representation of a FHIR code.""" if not fhir_types.is_type_or_profile_of_code(code): raise ValueError( f'Invalid type for get_code_as_string: {code.DESCRIPTOR.full_name}' ) value_field = code.DESCRIPTOR.fields_by_name.get('value') if value_field is None: raise ValueError( f'Invalid code type for get_code_as_string: {code.DESCRIPTOR.full_name}' ) value = proto_utils.get_value_at_field(code, value_field) if value_field.type == descriptor.FieldDescriptor.TYPE_STRING: return value elif value_field.type == descriptor.FieldDescriptor.TYPE_ENUM: return enum_value_descriptor_to_code_string( value_field.enum_type.values_by_number[value]) else: raise fhir_errors.InvalidFhirError( f'Invalid value field type: {value_field.type!r} for code: ' f'{code.DESCRIPTOR.full_name}')
def get_contained_resource( contained_resource: message.Message) -> message.Message: """Returns the resource instance contained within `contained_resource`. Args: contained_resource: The containing `ContainedResource` instance. Returns: The resource contained by `contained_resource`. Raises: TypeError: In the event that `contained_resource` is not of type `ContainedResource`. ValueError: In the event that the oneof on `contained_resource` is not set. """ # TODO: Use an annotation here. if contained_resource.DESCRIPTOR.name != 'ContainedResource': raise TypeError('Expected `ContainedResource` but got: ' f'{type(contained_resource)}.') oneof_field = contained_resource.WhichOneof('oneof_resource') if oneof_field is None: raise ValueError('`ContainedResource` oneof not set.') return proto_utils.get_value_at_field(contained_resource, oneof_field)
def _print_reference(self, reference: message.Message) -> None: """Standardizes and prints the provided reference. Note that "standardization" in the case of PURE FHIR JSON refers to un-typing the typed-reference prior to printing. Args: reference: The reference to print. """ set_oneof = reference.WhichOneof('reference') if (self.json_format == _FhirJsonFormat.PURE and set_oneof is not None and set_oneof != 'uri'): # In pure FHIR mode, we have to serialize structured references # into FHIR uri strings. standardized_reference = copy.copy(reference) # Setting the new URI field will overwrite the original oneof new_uri = proto_utils.get_value_at_field(standardized_reference, 'uri') proto_utils.set_value_at_field( new_uri, 'value', references.reference_to_string(reference)) self._print_message(standardized_reference) else: self._print_message(reference)
def copy_code(source: message.Message, target: message.Message): """Adds all fields from source to target. Args: source: The FHIR Code instance to copy from. target: The target FHIR Code instance to copy to. """ if not fhir_types.is_type_or_profile_of_code(source.DESCRIPTOR): raise fhir_errors.InvalidFhirError( f'Source: {source.DESCRIPTOR.full_name} ' 'is not type or profile of Code.') if not fhir_types.is_type_or_profile_of_code(target.DESCRIPTOR): raise fhir_errors.InvalidFhirError( f'Target: {target.DESCRIPTOR.full_name} ' 'is not type or profile of Code.') if proto_utils.are_same_message_type(source.DESCRIPTOR, target.DESCRIPTOR): target.CopyFrom(source) return source_value_field = source.DESCRIPTOR.fields_by_name.get('value') target_value_field = target.DESCRIPTOR.fields_by_name.get('value') if source_value_field is None or target_value_field is None: raise fhir_errors.InvalidFhirError( 'Unable to copy code from ' f'{source.DESCRIPTOR.full_name} ' f'to {target.DESCRIPTOR.full_name}.') proto_utils.copy_common_field(source, target, 'id') proto_utils.copy_common_field(source, target, 'extension') # Handle specialized codes if (source_value_field.type not in _CODE_TYPES or target_value_field.type not in _CODE_TYPES): raise ValueError( f'Unable to copy from {source.DESCRIPTOR.full_name} ' f'to {target.DESCRIPTOR.full_name}. Must have a field ' 'of TYPE_ENUM or TYPE_STRING.') source_value = proto_utils.get_value_at_field(source, source_value_field) if source_value_field.type == target_value_field.type: # Perform a simple assignment if value_field types are equivalent proto_utils.set_value_at_field(target, target_value_field, source_value) else: # Otherwise, we need to transform the value prior to assignment... if source_value_field.type == descriptor.FieldDescriptor.TYPE_STRING: source_enum_value = code_string_to_enum_value_descriptor( source_value, target_value_field.enum_type) proto_utils.set_value_at_field(target, target_value_field, source_enum_value.number) elif source_value_field.type == descriptor.FieldDescriptor.TYPE_ENUM: source_string_value = enum_value_descriptor_to_code_string( source_value_field.enum_type.values_by_number[source_value]) proto_utils.set_value_at_field(target, target_value_field, source_string_value) else: # Should never hit raise ValueError('Unexpected generic value field type: ' f'{source_value_field.type}. Must be a field of ' 'TYPE_ENUM or TYPE_STRING in order to copy.')
def testGetValueAtFieldName_invalidName_raisesException(self): arbitrary_string = datatypes_pb2.String(value="foo") with self.assertRaises(ValueError) as ve: proto_utils.get_value_at_field(arbitrary_string, "notvalue") self.assertIsInstance(ve.exception, ValueError)