Esempio n. 1
0
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)
Esempio n. 2
0
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
Esempio n. 3
0
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.')
Esempio n. 4
0
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))
Esempio n. 5
0
    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)
Esempio n. 6
0
  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
Esempio n. 7
0
  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)
Esempio n. 8
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.')
Esempio n. 9
0
    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)
Esempio n. 11
0
  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)
Esempio n. 12
0
 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)
Esempio n. 13
0
    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())
Esempio n. 14
0
    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'))
Esempio n. 15
0
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
Esempio n. 16
0
    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)
Esempio n. 17
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)
Esempio n. 18
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)
Esempio n. 19
0
    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'))
Esempio n. 20
0
    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)
Esempio n. 21
0
  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'))
Esempio n. 22
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)
Esempio n. 23
0
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)
Esempio n. 24
0
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)
Esempio n. 25
0
    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)
Esempio n. 26
0
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}')
Esempio n. 27
0
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)
Esempio n. 28
0
    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)
Esempio n. 29
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.')
Esempio n. 30
0
  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)