crs4 / hl7apy

Python library to parse, create and handle HL7 v2 messages.
http://crs4.github.io/hl7apy/
MIT License
231 stars 91 forks source link

OBX segment fails on one method of construction but not another. #62

Closed JeremyGibson closed 3 years ago

JeremyGibson commented 4 years ago

I have a class that builds out an HL7 2.5.1 message. For all other segments I build out the segment using the field value assignment method, but for some reason the OBX segment always fails using this method on assigning 'application/pdf' to OBX 5.2. The exception is that this is not a valid child of OBX5.

However,

If a use the _get_fields() method then the message validates and builds properly. `## For some reason assigning the OBX segment like this fails on validation

obx.obx_2.value = 'ED'

    # obx.obx_3.obx_3_1.value = '425392003'
    # obx.obx_3.obx_3_2.value = 'Active advance directive (finding)'
    # obx.obx_3.obx_3_3.value = 'SNOMED CT'
    # obx.obx_5.obx_5_2.value = 'application/pdf'
    # obx.obx_5.obx_5_4.value = 'BASE64'
    # obx.obx_5.obx_5_5.value = self._encode_pdf()

    elements = [
        ('OBX_2', 'ED'),
        ('OBX_3', '425392003^Active advance directive (finding)^SNOMED CT'),
        ('OBX_5', '^application/pdf^^BASE64^{}'.format(self._encode_pdf()))
    ]
    self._get_fields(elements, obx)
    self._message.add(obx)

def _get_fields(self, fields: list, segment: Segment): for name, value in fields: f = Field(name, version=self.VERSION) f.value = value segment.add(f) `

Deyspring commented 4 years ago

Hello, did you ever get this issue resolved? I have an identical problem and I don't know what's wrong.

Thanks, Katherine

JeremyGibson commented 4 years ago

@Deyspring I solved it originally using the list of tuples method above.

However after further investigation I've found that Hl7apy expects OBX segments (at least in an MDM_T02 message) to be children of Segment('MDM_T02_OBXNTE_SUPPGRP'). So I believe you could do something like this:

obx = Segment('OBX', version=2.5.1)
### build out your obx as you would normally
obx.obx_2.value = 'ED'
obx.obx_3.obx_3_1.value = '123467'
...
### This part I haven't tested at all
mdm = Segment('MDM_T02_OBXNTE_SUPPGRP', version=2.5.1)
mdm.children.add(obx)

Hope that helps.

Deyspring commented 4 years ago

It didn't work for obx_3 validation works for the first two fields but fails at the third one. It also works for obx.4, 5, 7,11,14 and 19, but none of the other fields. I did try the other method you described using

Here's the code.

def hl7_obx_segment(message:Message, data:dict): message_name = "ORU_R01" message = Message(message_name, version= "2.5.1") obx = Segment('OBX', version="2.5.1")

message.obx.obx_1 = "1" # SET ID -OBX message.obx.obx_2 = "CWE" # Value Type

hl7apy.exceptions.ChildNotFound: No child named OBX_3_1

message.obx.obx_3_1 = "43304-5"

The second method you mentioned didn't work either. When I ran it I got the following error:

segment_name = "MDM_T02_OBXNTE_SUPPGRP" mdm = Segment(segment_name, version="2.5.1") mdm.children.add(obx)

hl7apy.exceptions.InvalidName: Invalid name for Segment: MDM_T02_OBXNTE_SUPPGRP

Any thoughts are appreciated. Thanks!

On Tue, Jun 16, 2020 at 5:08 AM Jeremy Gibson notifications@github.com wrote:

@Deyspring https://github.com/Deyspring I solved it originally using the list of tuples method above.

However after further investigation I've found that Hl7apy expects OBX segments (at least in an MDM_T02) message to be children of Segment('MDM_T02_OBXNTE_SUPPGRP'). So I believe you could do something like this:

` obx = Segment('OBX', version=2.5.1) build out your obx as you would normally

obx.obx_2.value = 'ED' obx.obx_3.obx_3_1.value = '123467' ... This part I haven't tested at all

mdm = Segment('MDM_T02_OBXNTE_SUPPGRP', version=2.5.1) mdm.children.add(obx) ` Hope that helps.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/crs4/hl7apy/issues/62#issuecomment-644721951, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADAUAQ5YWML43653SMQSYC3RW5OC7ANCNFSM4KD2ABFA .

svituz commented 4 years ago

Hi @Deyspring, in the first method you need to go deeper into the segment before calling obx_3_1, as in @JeremyGibson's snippet. The only difference is that he's started from the Segment object. Starting from the message, the correct way of accessing obx_3_1 is:

message.obx.obx_3.obx_3_1

Or, if you prefer, accessing to the datatype component

message.obx.obx_3.ce_1

The second method is failing because that name is not a segment name but a group. In the @JeremyGibson snippet, he creates the segment obx and then attaches it to the group. Notice that the correct way to add the obx to the group is not using children.add but:

The following example creates two obx segments and attaches them to the group

obx = Segment('OBX', version='2.5.1')
obx.obx_2.value = 'ED'
obx.obx_3.obx_3_1.value = '123467'

another_obx = Segment('OBX', version='2.5.1')
another_obx.obx_2.value = 'ED'
another_obx.obx_3.obx_3_1.value = '56789'

name = 'MDM_T02_OBXNTE_SUPPGRP'
mdm_group = Group(name, version='2.5.1')
mdm_group.obx = obx
mdm_group.add(another_obx)
mdm_group.to_er7()

You get,

OBX||ED|123467\rOBX||ED|56789

You get the same result with this:

name = "MDM_T02_OBXNTE_SUPPGRP"
mdm_group = Group(name, version="2.5.1")
mdm_group.obx.obx_2.value = 'ED'
mdm_group.obx.obx_3.obx_3_1.value = '123467'
mdm_group.add_segment('OBX')   # Here you're adding another instance 
mdm_group.obx[1].obx_2.value = 'ED'  # notice you need to specify the index now
mdm_group.obx[1].obx_3.obx_3_1.value = '56789'
JeremyGibson commented 4 years ago

@svituz Thank you for the detailed explanation. I had assumed MDM_T02_OBXNTE_SUPPGRP was a Segment type.

Deyspring commented 4 years ago

Thank you for the help! I fixed the bit where I wasn't assigning deeply enough and that fixed some, but not all of the fields

I'm still having trouble with my code though. All of the obx instances I'm creating are first instances, to my knowledge. I have no duplicates.

message.obx.obx_3.obx_3_1 = "43304-5" # message.obx.obx_3.obx_3_2 = "Chlamydia trachomatis rRNA" # Text message.obx.obx_3.obx_3_3 = "LN" # Name of Coding System message.obx.obx_3.obx_3_4 = "400" # Alternate Identifier message.obx.obx_3.obx_3_5 = " CT Genprobe" # Alternate Text message.obx.obx_3.obx_3_6 = "L" # Name of alternate Coding System

hl7apy.exceptions.ChildNotFound: No child named CE_7

message.obx.obx_3.obx_3_7 = "2.34" #Coding System Version ID

field segments beyond obx_3 do work, but only the ones that have no subfields

On Wed, Jun 17, 2020 at 5:20 AM Jeremy Gibson notifications@github.com wrote:

@svituz https://github.com/svituz Thank you for the detailed explanation. I had assumed MDM_T02_OBXNTE_SUPPGRP was a Segment type.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/crs4/hl7apy/issues/62#issuecomment-645339878, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADAUAQ5CS3B24C33CHJC5SLRXCYH3ANCNFSM4KD2ABFA .

svituz commented 4 years ago

The thing here is that obx.3 datatype is CE, which has 6 Component. You get ChidNotFound exception because you're trying to create CE_7 component which does not exist in the standard. If you still want to add another component, you need to add an anonymous one by using the add() method.

message.obx.obx_3.obx_3_1 = "43304-5"  #
message.obx.obx_3.obx_3_2 = "Chlamydia trachomatis rRNA"  # Text
message.obx.obx_3.obx_3_3 = "LN"  # Name of Coding System
message.obx.obx_3.obx_3_4 = "400"  # Alternate Identifier
message.obx.obx_3.obx_3_5 = " CT Genprobe"  # Alternate Text
message.obx.obx_3.obx_3_6 = "L"  # Name of alternate Coding System
# hl7apy.exceptions.ChildNotFound: No child named CE_7
c = Component(datatype='ST', version='2.5.1')
c.value = '2.34'
message.obx.obx_3.add(c)
print(message.obx.to_er7())
print(message.obx.children)

You get

OBX|||43304-5^Chlamydia trachomatis rRNA^LN^400^ CT Genprobe^L^2.34
[<Component CE_1 (IDENTIFIER) of type ST>, <Component CE_2 (TEXT) of type ST>, <Component CE_3 (NAME_OF_CODING_SYSTEM) of type ID>, <Component CE_4 (ALTERNATE_IDENTIFIER) of type ST>, <Component CE_5 (ALTERNATE_TEXT) of type ST>, <Component CE_6 (NAME_OF_ALTERNATE_CODING_SYSTEM) of type ID>, <Component ST (None) of type ST>]

Notice that the last child Component has no name.

You can add other children, just keep in mind that children that are not in the structure will follow a FIFO behavior when serializing the message. Also be aware that, since you're not strictly following the standard, the validation will fail.

Deyspring commented 4 years ago

This helps a lot. I was trying to create the component incorrectly and had misunderstood what the client wanted. I didn't need all of the component types, just the ones that would validate. Thank you for the in depth explanation, I really appreciate it.

On Thu, Jun 18, 2020 at 1:27 AM Vittorio Meloni notifications@github.com wrote:

The thing here is that obx.3 datatype is CE, which has 6 Component. You get ChidNotFound exception because you're trying to create CE_7 component which does not exist in the standard. If you still want to add another component, you need to add an anonymous one by using the add() method.

message.obx.obx_3.obx_3_1 = "43304-5" # message.obx.obx_3.obx_3_2 = "Chlamydia trachomatis rRNA" # Text message.obx.obx_3.obx_3_3 = "LN" # Name of Coding System message.obx.obx_3.obx_3_4 = "400" # Alternate Identifier message.obx.obx_3.obx_3_5 = " CT Genprobe" # Alternate Text message.obx.obx_3.obx_3_6 = "L" # Name of alternate Coding System

hl7apy.exceptions.ChildNotFound: No child named CE_7

c = Component(datatype='ST', version='2.5.1') c.value = '2.34' message.obx.obx_3.add(c) print(message.obx.to_er7()) print(message.obx.children)

You get

OBX|||43304-5^Chlamydia trachomatis rRNA^LN^400^ CT Genprobe^L^2.34 [<Component CE_1 (IDENTIFIER) of type ST>, <Component CE_2 (TEXT) of type ST>, <Component CE_3 (NAME_OF_CODING_SYSTEM) of type ID>, <Component CE_4 (ALTERNATE_IDENTIFIER) of type ST>, <Component CE_5 (ALTERNATE_TEXT) of type ST>, <Component CE_6 (NAME_OF_ALTERNATE_CODING_SYSTEM) of type ID>, <Component ST (None) of type ST>]

Notice that the last child Component has no name.

You can add other children, just keep in mind that children that are not in the structure will follow a FIFO behavior when serializing the message. Also be aware that, since you're not strictly following the standard, the validation will fail.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/crs4/hl7apy/issues/62#issuecomment-645865275, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADAUAQ4OLXB65FNWHG4HZSDRXHFYFANCNFSM4KD2ABFA .