def get_element(self) -> Optional[message.Message]:
     """Return Element of the primitive. Xhtml cannot have extensions."""
     if not proto_utils.field_is_set(self.wrapped, 'id'):
         return None
     element = type(self.wrapped)()
     proto_utils.copy_common_field(self.wrapped, element, 'id')
     return element
Пример #2
0
  def testCopyCommonField_notPresentInBothMessages_raisesException(self):
    """Tests copy_common_field with an invalid descriptor raises."""
    first_patient = patient_pb2.Patient(
        active=datatypes_pb2.Boolean(value=True))
    second_patient = patient_pb2.Patient(
        active=datatypes_pb2.Boolean(value=False))

    with self.assertRaises(ValueError) as ve:
      proto_utils.copy_common_field(first_patient, second_patient, "value")
    self.assertIsInstance(ve.exception, ValueError)
Пример #3
0
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)
Пример #4
0
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)
Пример #5
0
  def testCopyCommonField_differentMessageTypes_succeeds(self):
    """Tests that copy_common_field succeeds on a single Message field."""
    string_value = datatypes_pb2.String(id=datatypes_pb2.String(value="foo"))
    boolean_value = datatypes_pb2.Boolean(id=datatypes_pb2.String(value="bar"))

    # Before copy
    self.assertEqual(string_value.id.value, "foo")
    self.assertEqual(boolean_value.id.value, "bar")

    proto_utils.copy_common_field(string_value, boolean_value, "id")

    # After copy
    self.assertEqual(string_value.id.value, "foo")
    self.assertEqual(boolean_value.id.value, "foo")
Пример #6
0
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.')
Пример #7
0
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)
Пример #8
0
  def get_element(self) -> Optional[message.Message]:
    """Returns the raw Element underlying the wrapped primitive.

    Note that conversion-only extensions are removed prior to returning.
    """
    if not (proto_utils.field_is_set(self.wrapped, 'id') or
            proto_utils.field_is_set(self.wrapped, 'extension')):
      return None  # Early-exit if we can't populate an Element

    element = proto_utils.create_message_from_descriptor(
        self.wrapped.DESCRIPTOR)
    if proto_utils.field_is_set(self.wrapped, 'id'):
      proto_utils.copy_common_field(self.wrapped, element, 'id')

    extensions_list = cast(List[Any],
                           extensions.get_fhir_extensions(self.wrapped))
    for extension in extensions_list:
      if extension.url.value not in extensions.CONVERSION_ONLY_EXTENSION_URLS:
        proto_utils.append_value_at_field(element, 'extension', extension)

    return element
Пример #9
0
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.')
Пример #10
0
def split_if_relative_reference(reference: message.Message):
  """If possible, parses a `Reference` `uri` into more structured fields.

  This is only possible for two forms of reference uris:
  * Relative references of the form $TYPE/$ID, e.g., "Patient/1234"
    In this case, this will be parsed to a proto of the form:
    {patient_id: {value: "1234"}}
  * Fragments of the form "#$FRAGMENT", e.g., "#vs1".  In this case, this would
    be parsed into a proto of the form:
    {fragment: {value: "vs1"} }

  If the reference URI matches one of these schemas, the `uri` field will be
  cleared, and the appropriate structured fields set. Otherwise, the reference
  will be unchanged.

  Args:
    reference: The FHIR reference to potentially split.

  Raises:
    ValueError: If the message is not a valid FHIR Reference proto.
  """
  _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)

  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