Module Gnumed.business.gmSOAPimporter

GNUmed SOAP importer

(specification by Karsten Hilbert Karsten.Hilbert@gmx.net)

This script is designed for importing GNUmed SOAP input "bundles".

    - "bundle" is list of dicts
    - each "bundle" is processed dict by dict
    - the dicts in the list are INDEPENDENT of each other
    - each dict contains information for one new clin_narrative row
    - each dict has the keys: 'soap', 'types', 'text', 'clin_context'
            - 'soap':
                    - relates to clin_narrative.soap_cat
            - 'types':
                    - a list of strings
                    - the strings must be found in clin_item_type.type
                    - strings not found in clin_item_type.type are ignored during
                      import and the user is warned about that
            - 'text':
                    - the narrative for clin_narrative.narrative, imported as is
            - 'clin_context':
                    - 'clin_context' is a dictionary containing clinical
                      context information, required to properly create clinical items.
                      Its 'episode_id' must always be supplied.
Expand source code
"""GNUmed SOAP importer

(specification by Karsten Hilbert <Karsten.Hilbert@gmx.net>)

This script is designed for importing GNUmed SOAP input "bundles".

        - "bundle" is list of dicts
        - each "bundle" is processed dict by dict
        - the dicts in the list are INDEPENDENT of each other
        - each dict contains information for one new clin_narrative row
        - each dict has the keys: 'soap', 'types', 'text', 'clin_context'
                - 'soap':
                        - relates to clin_narrative.soap_cat
                - 'types':
                        - a list of strings
                        - the strings must be found in clin_item_type.type
                        - strings not found in clin_item_type.type are ignored during
                          import and the user is warned about that
                - 'text':
                        - the narrative for clin_narrative.narrative, imported as is
                - 'clin_context':
                        - 'clin_context' is a dictionary containing clinical
                          context information, required to properly create clinical items.
                          Its 'episode_id' must always be supplied.
"""
#===============================================================
__author__ = "Carlos Moro <cfmoro1976@yahoo.es>"
__license__ = "GPL v2 or later (details at https://www.gnu.org)"

# stdlib
import sys, logging


# GNUmed
from Gnumed.pycommon import gmDispatcher
from Gnumed.business import gmClinNarrative, gmPerson, gmPersonSearch


_log = logging.getLogger('gm.soap')


# module level constants
soap_bundle_SOAP_CAT_KEY = "soap"
soap_bundle_TYPES_KEY = "types"
soap_bundle_TEXT_KEY = "text"
soap_bundle_CLIN_CTX_KEY = "clin_context"
soap_bundle_TYPE_KEY = "type"
soap_bundle_EPISODE_ID_KEY = "episode_id"
soap_bundle_ENCOUNTER_ID_KEY = "encounter_id"
soap_bundle_STAFF_ID_KEY = "staff_id"
soap_bundle_SOAP_CATS = ['s','o','a','p']               # these are pretty much fixed
#===============================================================
class cSOAPImporter:
        """
        Main SOAP importer class
        """
        #-----------------------------------------------------------
        def __init__(self):
                pass
        #-----------------------------------------------------------
        # external API
        #-----------------------------------------------------------
        def import_soap(self, bundle=None):
                """
                Import supplied GNUmed SOAP input "bundle". For details consult current
                module's description information.

                @param bundle: GNUmed SOAP input data (as described in module's information)
                @type bundle: list of dicts
                """
                # process each entry in soap bundle independently
                for soap_entry in bundle:
                        if not self.__import_narrative(soap_entry):
                                _log.error('skipping soap entry')
                                continue
                gmDispatcher.send(signal = 'clin_item_updated')
                return True
        #-----------------------------------------------------------
        # internal helpers
        #-----------------------------------------------------------
        def __import_narrative(self, soap_entry):
                """Import soap entry into GNUmed backend.

                @param soap_entry: dictionary containing information related
                                                   to one SOAP input line
                @type soap_entry: dictionary with keys 'soap', 'types', 'text'

                FIXME: Later we may want to allow for explicitly setting a staff ID to be
                FIXME: used for import. This would allow to import data "on behalf of" someone.
                """
                if not self.__verify_soap_entry(soap_entry=soap_entry):
                        _log.error('cannot verify soap entry')
                        return False
                # obtain clinical context information
                emr = gmPerson.gmCurrentPatient().emr
                epi_id = soap_entry[soap_bundle_CLIN_CTX_KEY][soap_bundle_EPISODE_ID_KEY]
                try:
                        enc_id = soap_entry[soap_bundle_CLIN_CTX_KEY][soap_bundle_ENCOUNTER_ID_KEY]
                except KeyError:
                        enc = emr.active_encounter
                        enc_id = enc['pk_encounter']

                # create narrative row
                narr = gmClinNarrative.create_narrative_item (
                        narrative = soap_entry[soap_bundle_TEXT_KEY],
                        soap_cat = soap_entry[soap_bundle_SOAP_CAT_KEY],
                        episode_id = epi_id,
                        encounter_id = enc_id
                )

#               # attach types
#               if soap_bundle_TYPES_KEY in soap_entry:
#                       print "code missing to attach types to imported narrative"

                return (narr is not None)
        #-----------------------------------------------------------
        def __verify_soap_entry(self, soap_entry):
                """Perform basic integrity check of a supplied SOAP entry.

                @param soap_entry: dictionary containing information related to one
                                                   SOAP input
                @type soap_entry: dictionary with keys 'soap', 'types', 'text'
                """
                required_keys = [
                        soap_bundle_SOAP_CAT_KEY,
                        soap_bundle_CLIN_CTX_KEY,
                        soap_bundle_TEXT_KEY
                ]
                # verify key existence
                for a_key in required_keys:
                        try:
                                soap_entry[a_key]
                        except KeyError:
                                _log.error('key [%s] is missing from soap entry' % a_key)
                                _log.error('%s' % soap_entry)
                                return False
                # verify key *values*
                if not soap_entry[soap_bundle_SOAP_CAT_KEY] in soap_bundle_SOAP_CATS:
                        _log.error('invalid soap category [%s]' % soap_entry[soap_bundle_SOAP_CAT_KEY])
                        _log.error('%s' % soap_entry)
                        return False
                try:
                        soap_entry[soap_bundle_CLIN_CTX_KEY][soap_bundle_EPISODE_ID_KEY]
                except KeyError:
                        _log.error('SOAP entry does not provide mandatory episode ID')
                        _log.error('%s' % soap_entry)
                        return False
                return True
        #-----------------------------------------------------------
#       def _verify_types(self, soap_entry):
#               """
#               Perform types key check of a supplied SOAP entry
#
#               @param soap_entry: dictionary containing information related to one
#                                                  SOAP input
#               @type soap_entry: dictionary with keys 'soap', 'types', 'text'
#               """
#
#               # FIXME fetch from backend
#               allowed_types = ['Hx']
#               for input_type in soap_entry[soap_bundle_TYPES_KEY]:
#                       if not input_type in allowed_types:
#                               _log.error('bad clin_item_type.type in supplied soap entry [%s]' % 
#                               soap_entry)
#                               return False
#               return True
        
#================================================================
# MAIN
#----------------------------------------------------------------
if __name__ == '__main__':
        _log.info("starting SOAP importer...")

        # obtain patient
        patient = gmPersonSearch.ask_for_patient()
        if patient is None:
                print("No patient. Exiting gracefully...")
                sys.exit(0)
        gmPerson.set_active_patient(patient=patient)

        # now import
        importer = cSOAPImporter()
        bundle = [
                {soap_bundle_SOAP_CAT_KEY: 's',
                 soap_bundle_TYPES_KEY: ['Hx'],
                 soap_bundle_TEXT_KEY: 'Test subjective narrative',
                 soap_bundle_CLIN_CTX_KEY: {soap_bundle_EPISODE_ID_KEY: '1'}
                },
                {soap_bundle_SOAP_CAT_KEY: 'o',
                 soap_bundle_TYPES_KEY: ['Hx'],
                 soap_bundle_TEXT_KEY: 'Test objective narrative',
                 soap_bundle_CLIN_CTX_KEY: {soap_bundle_EPISODE_ID_KEY: '1'}
                },
                {soap_bundle_SOAP_CAT_KEY: 'a',
                 soap_bundle_TYPES_KEY: ['Hx'],
                 soap_bundle_TEXT_KEY: 'Test assesment narrative',
                 soap_bundle_CLIN_CTX_KEY: {soap_bundle_EPISODE_ID_KEY: '1'}
                },
                {soap_bundle_SOAP_CAT_KEY: 'p',
                 soap_bundle_TYPES_KEY: ['Hx'],
                 soap_bundle_TEXT_KEY: 'Test plan narrative. [:tetanus:]. [:pneumoniae:].',
                 soap_bundle_CLIN_CTX_KEY: {
                        soap_bundle_EPISODE_ID_KEY: '1',
                        soap_bundle_ENCOUNTER_ID_KEY: '1',
                        soap_bundle_STAFF_ID_KEY: '1'
                        },
                }
        ]
        importer.import_soap(bundle)

        # clean up
        if patient is not None:
                try:
                        patient.cleanup()
                except Exception:
                        print("error cleaning up patient")

        _log.info("closing SOAP importer...")
#================================================================

Classes

class cSOAPImporter

Main SOAP importer class

Expand source code
class cSOAPImporter:
        """
        Main SOAP importer class
        """
        #-----------------------------------------------------------
        def __init__(self):
                pass
        #-----------------------------------------------------------
        # external API
        #-----------------------------------------------------------
        def import_soap(self, bundle=None):
                """
                Import supplied GNUmed SOAP input "bundle". For details consult current
                module's description information.

                @param bundle: GNUmed SOAP input data (as described in module's information)
                @type bundle: list of dicts
                """
                # process each entry in soap bundle independently
                for soap_entry in bundle:
                        if not self.__import_narrative(soap_entry):
                                _log.error('skipping soap entry')
                                continue
                gmDispatcher.send(signal = 'clin_item_updated')
                return True
        #-----------------------------------------------------------
        # internal helpers
        #-----------------------------------------------------------
        def __import_narrative(self, soap_entry):
                """Import soap entry into GNUmed backend.

                @param soap_entry: dictionary containing information related
                                                   to one SOAP input line
                @type soap_entry: dictionary with keys 'soap', 'types', 'text'

                FIXME: Later we may want to allow for explicitly setting a staff ID to be
                FIXME: used for import. This would allow to import data "on behalf of" someone.
                """
                if not self.__verify_soap_entry(soap_entry=soap_entry):
                        _log.error('cannot verify soap entry')
                        return False
                # obtain clinical context information
                emr = gmPerson.gmCurrentPatient().emr
                epi_id = soap_entry[soap_bundle_CLIN_CTX_KEY][soap_bundle_EPISODE_ID_KEY]
                try:
                        enc_id = soap_entry[soap_bundle_CLIN_CTX_KEY][soap_bundle_ENCOUNTER_ID_KEY]
                except KeyError:
                        enc = emr.active_encounter
                        enc_id = enc['pk_encounter']

                # create narrative row
                narr = gmClinNarrative.create_narrative_item (
                        narrative = soap_entry[soap_bundle_TEXT_KEY],
                        soap_cat = soap_entry[soap_bundle_SOAP_CAT_KEY],
                        episode_id = epi_id,
                        encounter_id = enc_id
                )

#               # attach types
#               if soap_bundle_TYPES_KEY in soap_entry:
#                       print "code missing to attach types to imported narrative"

                return (narr is not None)
        #-----------------------------------------------------------
        def __verify_soap_entry(self, soap_entry):
                """Perform basic integrity check of a supplied SOAP entry.

                @param soap_entry: dictionary containing information related to one
                                                   SOAP input
                @type soap_entry: dictionary with keys 'soap', 'types', 'text'
                """
                required_keys = [
                        soap_bundle_SOAP_CAT_KEY,
                        soap_bundle_CLIN_CTX_KEY,
                        soap_bundle_TEXT_KEY
                ]
                # verify key existence
                for a_key in required_keys:
                        try:
                                soap_entry[a_key]
                        except KeyError:
                                _log.error('key [%s] is missing from soap entry' % a_key)
                                _log.error('%s' % soap_entry)
                                return False
                # verify key *values*
                if not soap_entry[soap_bundle_SOAP_CAT_KEY] in soap_bundle_SOAP_CATS:
                        _log.error('invalid soap category [%s]' % soap_entry[soap_bundle_SOAP_CAT_KEY])
                        _log.error('%s' % soap_entry)
                        return False
                try:
                        soap_entry[soap_bundle_CLIN_CTX_KEY][soap_bundle_EPISODE_ID_KEY]
                except KeyError:
                        _log.error('SOAP entry does not provide mandatory episode ID')
                        _log.error('%s' % soap_entry)
                        return False
                return True
        #-----------------------------------------------------------

Methods

def import_soap(self, bundle=None)

Import supplied GNUmed SOAP input "bundle". For details consult current module's description information.

@param bundle: GNUmed SOAP input data (as described in module's information) @type bundle: list of dicts

Expand source code
def import_soap(self, bundle=None):
        """
        Import supplied GNUmed SOAP input "bundle". For details consult current
        module's description information.

        @param bundle: GNUmed SOAP input data (as described in module's information)
        @type bundle: list of dicts
        """
        # process each entry in soap bundle independently
        for soap_entry in bundle:
                if not self.__import_narrative(soap_entry):
                        _log.error('skipping soap entry')
                        continue
        gmDispatcher.send(signal = 'clin_item_updated')
        return True