def testAreSameMessageType_withDifferentMessageType_returnsFalse(self): """Test are_same_message_type with two different message types.""" patient = patient_pb2.Patient() uscore_patient_profile = uscore_pb2.USCorePatientProfile() self.assertFalse( proto_utils.are_same_message_type(patient.DESCRIPTOR, uscore_patient_profile.DESCRIPTOR)) self.assertFalse( proto_utils.are_same_message_type(patient_pb2.Patient.DESCRIPTOR, uscore_patient_profile.DESCRIPTOR))
def testAreSameMessageType_withSameMessageType_returnsTrue(self): """Test are_same_message_type with the same message types.""" patient_a = patient_pb2.Patient() patient_b = patient_pb2.Patient() self.assertTrue( proto_utils.are_same_message_type(patient_a.DESCRIPTOR, patient_b.DESCRIPTOR)) self.assertTrue( proto_utils.are_same_message_type(patient_pb2.Patient.DESCRIPTOR, patient_pb2.Patient.DESCRIPTOR))
def testGetMessageClassFromDescriptor_returnsMessageClass(self): """Tests that the correct class is returned for a message.""" actual = proto_utils.get_message_class_from_descriptor( patient_pb2.Patient.DESCRIPTOR) self.assertTrue( proto_utils.are_same_message_type(actual.DESCRIPTOR, patient_pb2.Patient.DESCRIPTOR))
def __init__(self, wrapped: message.Message, context: Context): """Converts wrapped into an instance of context.code_cls and wraps.""" if not proto_utils.are_same_message_type(wrapped.DESCRIPTOR, context.code_cls.DESCRIPTOR): tmp = context.code_cls() codes.copy_code(wrapped, tmp) wrapped = tmp super(CodeWrapper, self).__init__(wrapped, context)
def merge_into(self, target: message.Message) -> None: """Merges the underlying wrapped primitive into target.""" if not proto_utils.are_same_message_type(self.wrapped.DESCRIPTOR, target.DESCRIPTOR): raise ValueError(f'Type mismatch in merge_into. Attempted to merge ' f'{self.wrapped.DESCRIPTOR.full_name} into ' f'{target.DESCRIPTOR.full_name}.') target.MergeFrom(self.wrapped)
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 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 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 _add_extension_value_to_message(extension: message.Message, msg: message.Message, message_field: descriptor.FieldDescriptor): """Serialize the provided extension and add it to the message. Args: extension: The FHIR extension to serialize. msg: The message to add the serialized extension to. message_field: The field on the message to set. Raises: InvalidFhirError: In the event that the field to be set is not a singular message type, or if the provided extension is not singular (has nested extensions). """ if message_field.type != descriptor.FieldDescriptor.TYPE_MESSAGE: raise fhir_errors.InvalidFhirError( f'{msg.DESCRIPTOR.full_name} is not a FHIR extension type.') extension_field = extension.DESCRIPTOR.fields_by_name['extension'] if proto_utils.field_content_length(extension, extension_field) > 0: raise fhir_errors.InvalidFhirError( 'No child extensions should be set on ' f'{extension.DESCRIPTOR.full_name}.') value_field = _get_populated_extension_value_field(extension) # If a choice type, need to assign the extension value to the correct field. if annotation_utils.is_choice_type_field(message_field): choice_message = proto_utils.get_value_at_field(msg, message_field) choice_descriptor = choice_message.DESCRIPTOR for choice_field in choice_descriptor.fields: if (value_field.message_type.full_name == choice_field.message_type.full_name): _add_extension_value_to_message(extension, choice_message, choice_field) return raise ValueError( f'No field on Choice Type {choice_descriptor.full_name} ' f'for extension {extension.DESCRIPTOR.full_name}.') # If the target message is a bound Code type, we need to convert the generic # Code field from the extension into the target typed Code. if annotation_utils.has_fhir_valueset_url(message_field.message_type): typed_code = proto_utils.set_in_parent_or_add(msg, message_field) codes.copy_code(cast(Any, extension).value.code, typed_code) return # If the target message is bound to a Coding type, we must convert the generic # Coding field from the extension into the target typed Coding. if fhir_types.is_type_or_profile_of_coding(message_field.message_type): typed_coding = proto_utils.set_in_parent_or_add(msg, message_field) codes.copy_coding(cast(Any, extension).value.coding, typed_coding) return # Value types must match if not proto_utils.are_same_message_type(value_field.message_type, message_field.message_type): raise ValueError( 'Missing expected value of type ' f'{message_field.message_type.full_name} in extension ' f'{extension.DESCRIPTOR.full_name}.') value = proto_utils.get_value_at_field( cast(Any, extension).value, value_field) if proto_utils.field_is_repeated(message_field): proto_utils.append_value_at_field(msg, message_field, value) else: proto_utils.set_value_at_field(msg, message_field, value)
def get_primitive_wrapper_cls_for_primitive_cls( self, primitive_cls: Type[message.Message] ) -> Type[_primitive_wrappers.PrimitiveWrapper]: if fhir_types.is_type_or_profile_of_code(primitive_cls.DESCRIPTOR): return _primitive_wrappers.CodeWrapper elif proto_utils.are_same_message_type( primitive_cls.DESCRIPTOR, self.base64_binary_cls.DESCRIPTOR): return _base64_binary.Base64BinaryWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.boolean_cls.DESCRIPTOR): return _primitive_wrappers.BooleanWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.date_cls.DESCRIPTOR): return _date.DateWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.canonical_cls.DESCRIPTOR): return _primitive_wrappers.StringLikePrimitiveWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.date_time_cls.DESCRIPTOR): return _date_time.DateTimeWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.decimal_cls.DESCRIPTOR): return _decimal.DecimalWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.id_cls.DESCRIPTOR): return _primitive_wrappers.StringLikePrimitiveWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.instant_cls.DESCRIPTOR): return _instant.InstantWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.integer_cls.DESCRIPTOR): return _primitive_wrappers.IntegerLikePrimitiveWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.markdown_cls.DESCRIPTOR): return _primitive_wrappers.StringLikePrimitiveWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.oid_cls.DESCRIPTOR): return _primitive_wrappers.StringLikePrimitiveWrapper elif proto_utils.are_same_message_type( primitive_cls.DESCRIPTOR, self.positive_int_cls.DESCRIPTOR): return _primitive_wrappers.IntegerLikePrimitiveWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.string_cls.DESCRIPTOR): return _primitive_wrappers.StringLikePrimitiveWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.time_cls.DESCRIPTOR): return _time.TimeWrapper elif proto_utils.are_same_message_type( primitive_cls.DESCRIPTOR, self.unsigned_int_cls.DESCRIPTOR): return _primitive_wrappers.IntegerLikePrimitiveWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.uri_cls.DESCRIPTOR): return _primitive_wrappers.StringLikePrimitiveWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.url_cls.DESCRIPTOR): return _primitive_wrappers.StringLikePrimitiveWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.uuid_cls.DESCRIPTOR): return _primitive_wrappers.StringLikePrimitiveWrapper elif proto_utils.are_same_message_type(primitive_cls.DESCRIPTOR, self.xhtml_cls.DESCRIPTOR): return _primitive_wrappers.XhtmlWrapper raise ValueError( f'Unexpected R4 FHIR primitive: {primitive_cls.DESCRIPTOR.full_name!r} ' f'for handler: {type(self)}.')