lib7 usage ========== This is a high level implementation for python3 of lib7_. Source code of this implementation can be found here_. .. _lib7: https://gitlab.com/wunderlins/hl7parse/ .. _here: https://gitlab.com/wunderlins/hl7parse/ `lib7` is a C implementation of an hl7 parser and composer. The parsing part has the goal of being resource and runtime efficient. The python3 implementation is an interface to this library for rapid prototyping and experimentation. This comes at a cost, runtime efficiency is not as good as with the C implementation (but it's stil lfaster than a pure python implementation), however, writing code is much quicker. You may use :py:class:`lib7.Message.__init__` or :py:meth:`lib7.open_file` to create a :py:class:`lib7.Message` Object for reading. see `Reading HL7 Files`_. :py:meth:`lib7.compose` or :py:class:`lib7.Composer` will create an object that let's you manipulate a hl7 file. However, this method is slower and is, therefore, not optimal when parsing many files without the need of modifying the content. see `Composing HL7 Files`_. Understanding Addressing ------------------------ You can either iterate trough all objects to find a node or you may use the `get(addr)` and `set(addr, value)` methods to get and set values. It is important to understand, that the HL7 structure actually has 6 hirarchy levels (most people forget FIELD repetitions `~` and SEGMENT repetitions, ie. multiple `OBR` segments). :: MSH|...||||||||ORU&data^R01~rep1 MSH(1)-9(1).2.1 <--- all optional address values ^ ^ ^ ^ ^ ^ | | | | | +---- SUBCOMP | | | | +------ COMPONENT | | | +--------- FIELD (optional? FIXME set default to 1) | | +----------- FIELDLIST | +-------------- SEGMENT repetition (optional, defaults to 1) +----------------- SEGMENT MSH SEGMENT | |||||||| FIELDLIST ... ~rep1 FIELD ORU ^R01 COMPONENT &data SUBCMP Addressing in HL7 (as it is used) is often ambigous. The 2 cases where tis is true is by addressing SEGMENTs and FIELDs. As an example: :: REP|0|f1~rep1|f2 REP|1|f21|f2~rep2 Here we see the 2 repetitions: - a segment can occour multiple times in a hl7 message (typically `OBR`) or in this case `REP` - a field (typically delimited by `|` can have multiple repetitions delimited by `~` Often the value `f1` is address by `REP-2` but this would return `f1~rep1` from the first line and `f21` from the 2nd line. The correct addres to find `f1` would be: :: REP(1)-2(1) or to find `f21` :: REP(2)-2(1) Reading HL7 Files ----------------- :: import lib7 msg = lib7.open("path/to/some/file.hl7") # check what sort of message type this is, defined in MsH-9.1 msh91 = None msh92 = None try: msh91 = msg.get("MSH-9.1") print(msh91) # should print the content of the FIELD except Hl7StructureException: print("MSH-9.1 Not found) try: msh92 = msh91.next_sibling except Hl7StructureException: print("MSH-9.2 Not found) root = msh92 while root.parent: root = root.parent # we have now found the top most node which is of type MESSAGE # This node contains N segments in children print(root.type) # prints: Type.MESSAGE print("%r" % root) # prints: print(root.num_children) # prints: 2 assert(root == msg) # same same but different (variable name) # every node in the structure is an iterator which will # iterate over all child nodes. # # This is the preferred way, because it is faster than first # fetching all nodes via `n.children` and then looping over them. for seg in root: print("%r" % seg) # prints: # print(seg.addr) # prints: MSH(1) # PID(1) # or you may fetch a list of child ndoes like this: children = root.children # print the segment names for c in children: print(c.data) # prints: MSH, PID, ... See also: - :py:meth:`lib7.Message.get`: get a node by address - :py:meth:`lib7.Message.meta`: hl7 metadata such as separators, see: :py:class:`lib7.Meta` - :py:attr:`lib7.Message.children`: a list of segments in this `Message` - :py:attr:`lib7.Node.data`: the nodes data if it has no children ( `num_children` is 0) - :py:attr:`lib7.Node.addr`: string representation of the current node's address - :py:attr:`lib7.Node.parent`: the parent Node, `None` if there is no parent - :py:attr:`lib7.Node.children`: a list of child ndoes - :py:attr:`lib7.Node.next_sibling`: the next sibling of this ndoe - :py:attr:`lib7.Node.previous_sibling`: the last sibling of this node - :py:attr:`lib7.Node.first_child`: gets the first child `Node` - :py:attr:`lib7.Node.last_child`: gets the last child `Node` - :py:meth:`lib7.Node.child_at`: get a specific child at position `N` - :py:attr:`lib7.Node.num_children`: how many child ndoes are there ? - :py:attr:`lib7.Node.num_siblings`: how many siblings are there ? - :py:attr:`lib7.Node.type`: returns the type :py:class:`lib7.Type` - :py:class:`lib7.Node`: every child of `Message` is a `Node` Composing HL7 Files ------------------- With :py:class:`lib7.Composer` you get access to writing and creating hl7 files. because :py:class:`lib7.Composer` derives from :py:class:`lib7.Message`, all the above methods described in `Reading HL7 Files`_ are applicable. :: import lib7 # open an existing file for modifying msg = lib7.compose("path/to/some/file.hl7") # will create segment: "XYZ|^&mydata\r" msg.set("XYZ(1)-1.2.3, "mydata") # now write the changed data msg.write_file("path/to/some/other.hl7") # if you do bulk editing over many files, it is a good # idea to free memory like so: del(msg) # create an empty message. msg = lib7.compose() # this message will already contain an MSH segment, always, # it's really needed # # now you can start adding stuff msg.set("PID-3(1), "patientnumber") del(msg) See also: - :py:meth:`lib7.Composer.set`: set a node by address, it will be created if it doesn't exist - :py:meth:`lib7.Composer.write_file`: write the created or modified file - :py:class:`lib7.Message`: all the other methods inherited from `Message` - :py:class:`lib7.Node`: every child of `Message` is a `Node`