Gnumed.business.gmProviderInbox API documentation
Description: GNUmed provider inbox middleware …

# Module `Gnumed.business.gmProviderInbox`

GNUmed provider inbox middleware.

This should eventually end up in a class cPractice.

Expand source code

    # -*- coding: latin-1 -*-
    """GNUmed provider inbox middleware.

    This should eventually end up in a class cPractice.
    """
    #============================================================
    __license__ = "GPL"
    __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"

    import sys
    import logging

    if __name__ == '__main__':
            sys.path.insert(0, '../../')
            _ = lambda x:x
    from Gnumed.pycommon import gmPG2
    from Gnumed.pycommon import gmBusinessDBObject
    from Gnumed.pycommon import gmTools
    from Gnumed.pycommon import gmDateTime

    from Gnumed.business import gmStaff

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

    #============================================================
    # provider message inbox
    #------------------------------------------------------------
    _SQL_get_inbox_messages = """SELECT * FROM dem.v_message_inbox d_vi WHERE %s"""

    class cInboxMessage(gmBusinessDBObject.cBusinessDBObject):

            _cmd_fetch_payload = _SQL_get_inbox_messages % "pk_inbox_message = %s"
            _cmds_store_payload = [
                    """
                            UPDATE dem.message_inbox SET
                                    fk_staff = %(pk_staff)s,
                                    fk_inbox_item_type = %(pk_type)s,
                                    comment = gm.nullify_empty_string(%(comment)s),
                                    data = gm.nullify_empty_string(%(data)s),
                                    importance = %(importance)s,
                                    fk_patient = %(pk_patient)s,
                                    ufk_context = NULLIF(%(pk_context)s::integer[], ARRAY[NULL::integer]),
                                    due_date = %(due_date)s,
                                    expiry_date = %(expiry_date)s
                            WHERE
                                    pk = %(pk_inbox_message)s
                                            AND
                                    xmin = %(xmin_message_inbox)s
                            RETURNING
                                    pk as pk_inbox_message,
                                    xmin as xmin_message_inbox
                    """
            ]
            _updatable_fields = [
                    'pk_staff',
                    'pk_type',
                    'comment',
                    'data',
                    'importance',
                    'pk_patient',
                    'pk_context',
                    'due_date',
                    'expiry_date'
            ]
            #------------------------------------------------------------
            def format(self, with_patient=True):
                    tt = '%s: %s%s\n' % (
                            gmDateTime.pydt_strftime (
                                    self._payload['received_when'],
                                    format = '%A, %Y %b %d, %H:%M',
                                    accuracy = gmDateTime.acc_minutes
                            ),
                            gmTools.bool2subst(self._payload['is_virtual'], _('virtual message'), _('message')),
                            gmTools.coalesce(self._payload['pk_inbox_message'], '', ' #%s ')
                    )

                    tt += '%s: %s\n' % (
                            self._payload['l10n_category'],
                            self._payload['l10n_type']
                    )

                    tt += '%s %s %s\n' % (
                            self._payload['modified_by'],
                            gmTools.u_arrow2right,
                            gmTools.coalesce(self._payload['provider'], _('everyone'))
                    )

                    tt += '\n%s%s%s\n\n' % (
                            gmTools.u_left_double_angle_quote,
                            self._payload['comment'],
                            gmTools.u_right_double_angle_quote
                    )

                    if with_patient and (self._payload['pk_patient'] is not None):
                            tt += _('Patient: %s, %s%s %s   #%s\n' % (
                                    self._payload['lastnames'],
                                    self._payload['firstnames'],
                                    gmTools.coalesce(self._payload['l10n_gender'], '', ' (%s)'),
                                    gmDateTime.pydt_strftime(self._payload['dob'], '%Y %b %d', none_str = ''),
                                    self._payload['pk_patient']
                            ))

                    if self._payload['due_date'] is not None:
                            if self._payload['is_overdue']:
                                    template = _('Due: %s (%s ago)\n')
                            else:
                                    template = _('Due: %s (in %s)\n')
                            tt += template % (
                                    gmDateTime.pydt_strftime(self._payload['due_date'], '%Y %b %d'),
                                    gmDateTime.format_interval_medically(self._payload['interval_due'])
                            )

                    if self._payload['expiry_date'] is not None:
                            if self._payload['is_expired']:
                                    template = _('Expired: %s\n')
                            else:
                                    template = _('Expires: %s\n')
                            tt += template % gmDateTime.pydt_strftime(self._payload['expiry_date'], '%Y %b %d')

                    if self._payload['data'] is not None:
                            tt += self._payload['data'][:150]
                            if len(self._payload['data']) > 150:
                                    tt += gmTools.u_ellipsis

                    return tt
    #------------------------------------------------------------
    def get_reminders(pk_patient=None, order_by=None, return_pks=False):

            if order_by is None:
                    order_by = '%s ORDER BY due_date, importance DESC, received_when DESC'
            else:
                    order_by = '%%s ORDER BY %s' % order_by

            args = {'pat': pk_patient}
            where_parts = [
                    'pk_patient = %(pat)s',
                    'due_date IS NOT NULL'
            ]

            cmd = "SELECT * FROM dem.v_message_inbox WHERE %s" % (
                    order_by % ' AND '.join(where_parts)
            )
            _log.debug('SQL: %s', cmd)
            _log.debug('args: %s', args)
            rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
            if return_pks:
                    return [ r['pk_inbox_message'] for r in rows ]
            return [ cInboxMessage(row = {'data': r, 'pk_field': 'pk_inbox_message'}) for r in rows ]

    #------------------------------------------------------------
    def get_overdue_messages(pk_patient=None, order_by=None, return_pks=False):

            if order_by is None:
                    order_by = '%s ORDER BY due_date, importance DESC, received_when DESC'
            else:
                    order_by = '%%s ORDER BY %s' % order_by

            args = {'pat': pk_patient}
            where_parts = [
                    'pk_patient = %(pat)s',
                    'is_overdue IS TRUE'
            ]

            cmd = "SELECT * FROM dem.v_message_inbox WHERE %s" % (
                    order_by % ' AND '.join(where_parts)
            )
            _log.debug('SQL: %s', cmd)
            _log.debug('args: %s', args)
            rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
            if return_pks:
                    return [ r['pk_inbox_message'] for r in rows ]
            return [ cInboxMessage(row = {'data': r, 'pk_field': 'pk_inbox_message'}) for r in rows ]

    #------------------------------------------------------------
    def get_relevant_messages(pk_staff=None, pk_patient=None, include_without_provider=False, order_by=None, return_pks=False):

            if order_by is None:
                    order_by = '%s ORDER BY importance desc, received_when desc'
            else:
                    order_by = '%%s ORDER BY %s' % order_by

            args = {}
            where_parts = []

            if pk_staff is not None:
                    if include_without_provider:
                            where_parts.append('((pk_staff IN (%(staff)s, NULL)) OR (modified_by = (SELECT short_alias FROM dem.staff WHERE pk = %(staff)s)))')
                    else:
                            where_parts.append('((pk_staff = %(staff)s) OR (modified_by = (SELECT short_alias FROM dem.staff WHERE pk = %(staff)s)))')
                    args['staff'] = pk_staff

            if pk_patient is not None:
                    where_parts.append('pk_patient = %(pat)s')
                    args['pat'] = pk_patient

            where_parts.append("""
                    -- messages which have no due date and are not expired
                    ((due_date IS NULL) AND ((expiry_date IS NULL) OR (expiry_date > now())))
                            OR
                    -- messages which are due and not expired
                    ((due_date IS NOT NULL) AND (due_date < now()) AND ((expiry_date IS NULL) OR (expiry_date > now())))
            """)

            cmd = _SQL_get_inbox_messages % (
                    order_by % ' AND '.join(where_parts)
            )
            _log.debug('SQL: %s', cmd)
            _log.debug('args: %s', args)
            rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
            if return_pks:
                    return [ r['pk_inbox_message'] for r in rows ]
            return [ cInboxMessage(row = {'data': r, 'pk_field': 'pk_inbox_message'}) for r in rows ]

    #------------------------------------------------------------
    def get_inbox_messages(pk_staff=None, pk_patient=None, include_without_provider=False, exclude_expired=False, expired_only=False, overdue_only=False, unscheduled_only=False, exclude_unscheduled=False, order_by=None, return_pks=False):

            if order_by is None:
                    order_by = '%s ORDER BY importance desc, received_when desc'
            else:
                    order_by = '%%s ORDER BY %s' % order_by

            args = {}
            where_parts = []

            if pk_staff is not None:
                    if include_without_provider:
                            where_parts.append('((pk_staff IN (%(staff)s, NULL)) OR (modified_by = (SELECT short_alias FROM dem.staff WHERE pk = %(staff)s)))')
                    else:
                            where_parts.append('((pk_staff = %(staff)s) OR (modified_by = (SELECT short_alias FROM dem.staff WHERE pk = %(staff)s)))')
                    args['staff'] = pk_staff

            if pk_patient is not None:
                    where_parts.append('pk_patient = %(pat)s')
                    args['pat'] = pk_patient

            if exclude_expired:
                    where_parts.append('is_expired IS FALSE')

            if expired_only:
                    where_parts.append('is_expired IS TRUE')

            if overdue_only:
                    where_parts.append('is_overdue IS TRUE')

            if unscheduled_only:
                    where_parts.append('due_date IS NULL')

            if exclude_unscheduled:
                    where_parts.append('due_date IS NOT NULL')

            cmd = _SQL_get_inbox_messages % (
                    order_by % ' AND '.join(where_parts)
            )
            _log.debug('SQL: %s', cmd)
            _log.debug('args: %s', args)
            rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
            if return_pks:
                    return [ r['pk_inbox_message'] for r in rows ]
            return [ cInboxMessage(row = {'data': r, 'pk_field': 'pk_inbox_message'}) for r in rows ]

    #------------------------------------------------------------
    def create_inbox_message(message_type=None, subject=None, patient=None, staff=None, message_category='clinical'):

            success, pk_type = gmTools.input2int(initial = message_type)
            if not success:
                    pk_type = create_inbox_item_type(message_type = message_type, category = message_category)

            cmd = """
                    INSERT INTO dem.message_inbox (
                            fk_staff,
                            fk_patient,
                            fk_inbox_item_type,
                            comment
                    ) VALUES (
                            %(staff)s,
                            %(pat)s,
                            %(type)s,
                            gm.nullify_empty_string(%(subject)s)
                    )
                    RETURNING pk
            """
            args = {
                    'staff': staff,
                    'pat': patient,
                    'type': pk_type,
                    'subject': subject
            }
            rows = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
            return cInboxMessage(aPK_obj = rows[0]['pk'])

    #------------------------------------------------------------
    def delete_inbox_message(inbox_message=None):
            args = {'pk': inbox_message}
            cmd = "DELETE FROM dem.message_inbox WHERE pk = %(pk)s"
            gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
            return True

    #------------------------------------------------------------
    def create_inbox_item_type(message_type=None, category='clinical'):

            # determine category PK
            success, pk_cat = gmTools.input2int(initial = category)
            if not success:
                    args = {'cat': category}
                    cmd = """SELECT COALESCE (
                            (SELECT pk FROM dem.inbox_item_category WHERE _(description) = %(cat)s),
                            (SELECT pk FROM dem.inbox_item_category WHERE description = %(cat)s)
                    ) AS pk"""
                    rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
                    if rows[0]['pk'] is None:
                            cmd = "INSERT INTO dem.inbox_item_category (description) VALUES (%(cat)s) RETURNING pk"
                            rows = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
                            pk_cat = rows[0]['pk']
                    else:
                            pk_cat = rows[0]['pk']

            # find type PK or create type
            args = {'pk_cat': pk_cat, 'type': message_type}
            cmd = """SELECT COALESCE (
                    (SELECT pk FROM dem.inbox_item_type where fk_inbox_item_category = %(pk_cat)s AND _(description) = %(type)s),
                    (SELECT pk FROM dem.inbox_item_type where fk_inbox_item_category = %(pk_cat)s AND description = %(type)s)
            ) AS pk"""
            rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
            if rows[0]['pk'] is None:
                    cmd = """
                            INSERT INTO dem.inbox_item_type (
                                    fk_inbox_item_category,
                                    description,
                                    is_user
                            ) VALUES (
                                    %(pk_cat)s,
                                    %(type)s,
                                    TRUE
                            ) RETURNING pk"""
                    rows = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)

            return rows[0]['pk']

    #============================================================
    class cProviderInbox:
            def __init__(self, provider_id=None):
                    if provider_id is None:
                            self.__provider_id = gmStaff.gmCurrentProvider()['pk_staff']
                    else:
                            self.__provider_id = provider_id
            #--------------------------------------------------------
            def delete_message(self, pk=None):
                    return delete_inbox_message(inbox_message = pk)

            #--------------------------------------------------------
            def add_message(self, message_type=None, subject=None, patient=None):
                    return create_inbox_message (
                            message_type = message_type,
                            subject = subject,
                            patient = patient,
                            staff = self.__provider_id
                    )

            #--------------------------------------------------------
            # properties
            #--------------------------------------------------------
            def get_messages(self, pk_patient=None, include_without_provider=False, exclude_expired=False, expired_only=False, overdue_only=False, unscheduled_only=False, exclude_unscheduled=False, order_by=None):
                    return get_inbox_messages (
                            pk_staff = self.__provider_id,
                            pk_patient = pk_patient,
                            include_without_provider = include_without_provider,
                            exclude_expired = exclude_expired,
                            expired_only = expired_only,
                            overdue_only = overdue_only,
                            unscheduled_only = unscheduled_only,
                            exclude_unscheduled = exclude_unscheduled,
                            order_by = order_by
                    )

            messages = property(get_messages)

            #--------------------------------------------------------
            def get_relevant_messages(self, pk_patient=None, include_without_provider=True):
                    return get_relevant_messages (
                            pk_staff = self.__provider_id,
                            pk_patient = pk_patient,
                            include_without_provider = include_without_provider
                    )

    #============================================================
    if __name__ == '__main__':

            if len(sys.argv) < 2:
                    sys.exit()

            if sys.argv[1] != 'test':
                    sys.exit()

            from Gnumed.pycommon import gmI18N

            gmI18N.activate_locale()
            gmI18N.install_domain()

            from Gnumed.pycommon import gmLog2
            gmLog2.print_logfile_name()

            gmPG2.request_login_params(setup_pool = True)

            #---------------------------------------
            def test_inbox():
                    gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
                    inbox = cProviderInbox()
                    for msg in inbox.messages:
                            print(msg)
            #---------------------------------------
            def test_msg():
                    msg = cInboxMessage(aPK_obj = 1)
                    print(msg)
            #---------------------------------------
            def test_create_type():
                    print(create_inbox_item_type(message_type = 'test'))
            #---------------------------------------
            def test_due():
                    for msg in get_overdue_messages(pk_patient = 12):
                            print(msg.format())
            #---------------------------------------
            #test_inbox()
            #test_msg()
            #test_create_type()
            test_due()

    #============================================================

## Functions

`def create_inbox_item_type(message_type=None, category='clinical')`
  Expand source code

      def create_inbox_item_type(message_type=None, category='clinical'):

              # determine category PK
              success, pk_cat = gmTools.input2int(initial = category)
              if not success:
                      args = {'cat': category}
                      cmd = """SELECT COALESCE (
                              (SELECT pk FROM dem.inbox_item_category WHERE _(description) = %(cat)s),
                              (SELECT pk FROM dem.inbox_item_category WHERE description = %(cat)s)
                      ) AS pk"""
                      rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
                      if rows[0]['pk'] is None:
                              cmd = "INSERT INTO dem.inbox_item_category (description) VALUES (%(cat)s) RETURNING pk"
                              rows = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
                              pk_cat = rows[0]['pk']
                      else:
                              pk_cat = rows[0]['pk']

              # find type PK or create type
              args = {'pk_cat': pk_cat, 'type': message_type}
              cmd = """SELECT COALESCE (
                      (SELECT pk FROM dem.inbox_item_type where fk_inbox_item_category = %(pk_cat)s AND _(description) = %(type)s),
                      (SELECT pk FROM dem.inbox_item_type where fk_inbox_item_category = %(pk_cat)s AND description = %(type)s)
              ) AS pk"""
              rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
              if rows[0]['pk'] is None:
                      cmd = """
                              INSERT INTO dem.inbox_item_type (
                                      fk_inbox_item_category,
                                      description,
                                      is_user
                              ) VALUES (
                                      %(pk_cat)s,
                                      %(type)s,
                                      TRUE
                              ) RETURNING pk"""
                      rows = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)

              return rows[0]['pk']

`def create_inbox_message(message_type=None, subject=None, patient=None, staff=None, message_category='clinical')`
  Expand source code

      def create_inbox_message(message_type=None, subject=None, patient=None, staff=None, message_category='clinical'):

              success, pk_type = gmTools.input2int(initial = message_type)
              if not success:
                      pk_type = create_inbox_item_type(message_type = message_type, category = message_category)

              cmd = """
                      INSERT INTO dem.message_inbox (
                              fk_staff,
                              fk_patient,
                              fk_inbox_item_type,
                              comment
                      ) VALUES (
                              %(staff)s,
                              %(pat)s,
                              %(type)s,
                              gm.nullify_empty_string(%(subject)s)
                      )
                      RETURNING pk
              """
              args = {
                      'staff': staff,
                      'pat': patient,
                      'type': pk_type,
                      'subject': subject
              }
              rows = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
              return cInboxMessage(aPK_obj = rows[0]['pk'])

`def delete_inbox_message(inbox_message=None)`
  Expand source code

      def delete_inbox_message(inbox_message=None):
              args = {'pk': inbox_message}
              cmd = "DELETE FROM dem.message_inbox WHERE pk = %(pk)s"
              gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
              return True

`def get_inbox_messages(pk_staff=None, pk_patient=None, include_without_provider=False, exclude_expired=False, expired_only=False, overdue_only=False, unscheduled_only=False, exclude_unscheduled=False, order_by=None, return_pks=False)`
  Expand source code

      def get_inbox_messages(pk_staff=None, pk_patient=None, include_without_provider=False, exclude_expired=False, expired_only=False, overdue_only=False, unscheduled_only=False, exclude_unscheduled=False, order_by=None, return_pks=False):

              if order_by is None:
                      order_by = '%s ORDER BY importance desc, received_when desc'
              else:
                      order_by = '%%s ORDER BY %s' % order_by

              args = {}
              where_parts = []

              if pk_staff is not None:
                      if include_without_provider:
                              where_parts.append('((pk_staff IN (%(staff)s, NULL)) OR (modified_by = (SELECT short_alias FROM dem.staff WHERE pk = %(staff)s)))')
                      else:
                              where_parts.append('((pk_staff = %(staff)s) OR (modified_by = (SELECT short_alias FROM dem.staff WHERE pk = %(staff)s)))')
                      args['staff'] = pk_staff

              if pk_patient is not None:
                      where_parts.append('pk_patient = %(pat)s')
                      args['pat'] = pk_patient

              if exclude_expired:
                      where_parts.append('is_expired IS FALSE')

              if expired_only:
                      where_parts.append('is_expired IS TRUE')

              if overdue_only:
                      where_parts.append('is_overdue IS TRUE')

              if unscheduled_only:
                      where_parts.append('due_date IS NULL')

              if exclude_unscheduled:
                      where_parts.append('due_date IS NOT NULL')

              cmd = _SQL_get_inbox_messages % (
                      order_by % ' AND '.join(where_parts)
              )
              _log.debug('SQL: %s', cmd)
              _log.debug('args: %s', args)
              rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
              if return_pks:
                      return [ r['pk_inbox_message'] for r in rows ]
              return [ cInboxMessage(row = {'data': r, 'pk_field': 'pk_inbox_message'}) for r in rows ]

`def get_overdue_messages(pk_patient=None, order_by=None, return_pks=False)`
  Expand source code

      def get_overdue_messages(pk_patient=None, order_by=None, return_pks=False):

              if order_by is None:
                      order_by = '%s ORDER BY due_date, importance DESC, received_when DESC'
              else:
                      order_by = '%%s ORDER BY %s' % order_by

              args = {'pat': pk_patient}
              where_parts = [
                      'pk_patient = %(pat)s',
                      'is_overdue IS TRUE'
              ]

              cmd = "SELECT * FROM dem.v_message_inbox WHERE %s" % (
                      order_by % ' AND '.join(where_parts)
              )
              _log.debug('SQL: %s', cmd)
              _log.debug('args: %s', args)
              rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
              if return_pks:
                      return [ r['pk_inbox_message'] for r in rows ]
              return [ cInboxMessage(row = {'data': r, 'pk_field': 'pk_inbox_message'}) for r in rows ]

`def get_relevant_messages(pk_staff=None, pk_patient=None, include_without_provider=False, order_by=None, return_pks=False)`
  Expand source code

      def get_relevant_messages(pk_staff=None, pk_patient=None, include_without_provider=False, order_by=None, return_pks=False):

              if order_by is None:
                      order_by = '%s ORDER BY importance desc, received_when desc'
              else:
                      order_by = '%%s ORDER BY %s' % order_by

              args = {}
              where_parts = []

              if pk_staff is not None:
                      if include_without_provider:
                              where_parts.append('((pk_staff IN (%(staff)s, NULL)) OR (modified_by = (SELECT short_alias FROM dem.staff WHERE pk = %(staff)s)))')
                      else:
                              where_parts.append('((pk_staff = %(staff)s) OR (modified_by = (SELECT short_alias FROM dem.staff WHERE pk = %(staff)s)))')
                      args['staff'] = pk_staff

              if pk_patient is not None:
                      where_parts.append('pk_patient = %(pat)s')
                      args['pat'] = pk_patient

              where_parts.append("""
                      -- messages which have no due date and are not expired
                      ((due_date IS NULL) AND ((expiry_date IS NULL) OR (expiry_date > now())))
                              OR
                      -- messages which are due and not expired
                      ((due_date IS NOT NULL) AND (due_date < now()) AND ((expiry_date IS NULL) OR (expiry_date > now())))
              """)

              cmd = _SQL_get_inbox_messages % (
                      order_by % ' AND '.join(where_parts)
              )
              _log.debug('SQL: %s', cmd)
              _log.debug('args: %s', args)
              rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
              if return_pks:
                      return [ r['pk_inbox_message'] for r in rows ]
              return [ cInboxMessage(row = {'data': r, 'pk_field': 'pk_inbox_message'}) for r in rows ]

`def get_reminders(pk_patient=None, order_by=None, return_pks=False)`
  Expand source code

      def get_reminders(pk_patient=None, order_by=None, return_pks=False):

              if order_by is None:
                      order_by = '%s ORDER BY due_date, importance DESC, received_when DESC'
              else:
                      order_by = '%%s ORDER BY %s' % order_by

              args = {'pat': pk_patient}
              where_parts = [
                      'pk_patient = %(pat)s',
                      'due_date IS NOT NULL'
              ]

              cmd = "SELECT * FROM dem.v_message_inbox WHERE %s" % (
                      order_by % ' AND '.join(where_parts)
              )
              _log.debug('SQL: %s', cmd)
              _log.debug('args: %s', args)
              rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
              if return_pks:
                      return [ r['pk_inbox_message'] for r in rows ]
              return [ cInboxMessage(row = {'data': r, 'pk_field': 'pk_inbox_message'}) for r in rows ]

## Classes

`class cInboxMessage (aPK_obj: int | dict = None, row: dict = None, link_obj=None)`

  Represents business objects in the database.

  ## Rules

  - instances ARE ASSUMED TO EXIST in the database
  - PK construction (aPK_obj): DOES verify its existence on instantiation (fetching data fails)
  - Row construction (row): allowed by using a dict of pairs of field name: field value (PERFORMANCE improvement)
  - does NOT verify FK target existence
  - does NOT create new entries in the database
  - does NOT lazy-fetch fields on access

  Class scope SQL commands and variables:

  _cmd_fetch_payload:

  - must return exactly one row
  - WHERE clause argument values are expected in self.pk_obj (taken from init(aPK_obj))
  - must return xmin of all rows that _cmds_store_payload will be updating, so views must support the xmin columns of their underlying tables

  _cmds_store_payload:

  - one or multiple "update … set … where xmin_ = … and pk = …" statements which actually update the database from the data in self._payload,
  - the last query must refetch at least the XMIN values needed to detect concurrent updates, their field names had better be the same as in _cmd_fetch_payload,
  - the last query CAN return other fields which is particularly useful when those other fields are computed in the backend and may thus change upon save but will not have been set by the client code explicitly - this is only really of concern if the saved subclass is to be reused after saving rather than re-instantiated
  - when subclasses tend to live a while after save_payload() was called and they support computed fields (say, _(some_column) you need to return all columns (see cEncounter)

  _updatable_fields:

  - a list of fields available for update via object['field']

  Call init from child classes like so:

          super().__init__(aPK_obj = aPK_obj, row = row, link_obj = link_obj)

  ## Args

  `aPK_obj`
    retrieve data from backend
  - an scalar value the ._cmd_fetch_payload WHERE condition must be a simple column: "… WHERE pk_col = %s"
  - a dictionary of values the ._cmd_fetch_payload WHERE condition must consume the dictionary and produce a unique row
  `row`
    must hold the fields
  - data: list of column values for the row selected by ._cmd_fetch_payload (as returned by cursor.fetchone() in the DB-API)
  - pk_field: the name of the primary key column OR
  - pk_obj: a dictionary suitable for being passed to cursor.execute and holding the primary key values, used for composite PKs
  - for example:

        row = {
                'data': rows[0],
                'pk_field': 'pk_XXX (the PK column name)',
                'pk_obj': {'pk_col1': pk_col1_val, 'pk_col2': pk_col2_val}
        }

        rows = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
        objects = [ cChildClass(row = {'data': r, 'pk_field': 'the PK column name'}) for r in rows ]

  Expand source code

      class cInboxMessage(gmBusinessDBObject.cBusinessDBObject):

              _cmd_fetch_payload = _SQL_get_inbox_messages % "pk_inbox_message = %s"
              _cmds_store_payload = [
                      """
                              UPDATE dem.message_inbox SET
                                      fk_staff = %(pk_staff)s,
                                      fk_inbox_item_type = %(pk_type)s,
                                      comment = gm.nullify_empty_string(%(comment)s),
                                      data = gm.nullify_empty_string(%(data)s),
                                      importance = %(importance)s,
                                      fk_patient = %(pk_patient)s,
                                      ufk_context = NULLIF(%(pk_context)s::integer[], ARRAY[NULL::integer]),
                                      due_date = %(due_date)s,
                                      expiry_date = %(expiry_date)s
                              WHERE
                                      pk = %(pk_inbox_message)s
                                              AND
                                      xmin = %(xmin_message_inbox)s
                              RETURNING
                                      pk as pk_inbox_message,
                                      xmin as xmin_message_inbox
                      """
              ]
              _updatable_fields = [
                      'pk_staff',
                      'pk_type',
                      'comment',
                      'data',
                      'importance',
                      'pk_patient',
                      'pk_context',
                      'due_date',
                      'expiry_date'
              ]
              #------------------------------------------------------------
              def format(self, with_patient=True):
                      tt = '%s: %s%s\n' % (
                              gmDateTime.pydt_strftime (
                                      self._payload['received_when'],
                                      format = '%A, %Y %b %d, %H:%M',
                                      accuracy = gmDateTime.acc_minutes
                              ),
                              gmTools.bool2subst(self._payload['is_virtual'], _('virtual message'), _('message')),
                              gmTools.coalesce(self._payload['pk_inbox_message'], '', ' #%s ')
                      )

                      tt += '%s: %s\n' % (
                              self._payload['l10n_category'],
                              self._payload['l10n_type']
                      )

                      tt += '%s %s %s\n' % (
                              self._payload['modified_by'],
                              gmTools.u_arrow2right,
                              gmTools.coalesce(self._payload['provider'], _('everyone'))
                      )

                      tt += '\n%s%s%s\n\n' % (
                              gmTools.u_left_double_angle_quote,
                              self._payload['comment'],
                              gmTools.u_right_double_angle_quote
                      )

                      if with_patient and (self._payload['pk_patient'] is not None):
                              tt += _('Patient: %s, %s%s %s   #%s\n' % (
                                      self._payload['lastnames'],
                                      self._payload['firstnames'],
                                      gmTools.coalesce(self._payload['l10n_gender'], '', ' (%s)'),
                                      gmDateTime.pydt_strftime(self._payload['dob'], '%Y %b %d', none_str = ''),
                                      self._payload['pk_patient']
                              ))

                      if self._payload['due_date'] is not None:
                              if self._payload['is_overdue']:
                                      template = _('Due: %s (%s ago)\n')
                              else:
                                      template = _('Due: %s (in %s)\n')
                              tt += template % (
                                      gmDateTime.pydt_strftime(self._payload['due_date'], '%Y %b %d'),
                                      gmDateTime.format_interval_medically(self._payload['interval_due'])
                              )

                      if self._payload['expiry_date'] is not None:
                              if self._payload['is_expired']:
                                      template = _('Expired: %s\n')
                              else:
                                      template = _('Expires: %s\n')
                              tt += template % gmDateTime.pydt_strftime(self._payload['expiry_date'], '%Y %b %d')

                      if self._payload['data'] is not None:
                              tt += self._payload['data'][:150]
                              if len(self._payload['data']) > 150:
                                      tt += gmTools.u_ellipsis

                      return tt

  ### Ancestors

  - cBusinessDBObject

  ### Inherited members

  - `cBusinessDBObject`:
    - `fields_as_dict`
    - `format`
    - `get_fields`
    - `get_patient`
    - `get_updatable_fields`
    - `is_modified`
    - `keys`
    - `patient_pk`
    - `refetch_payload`
    - `same_payload`
    - `save`
    - `save_payload`
    - `staff_id`
`class cProviderInbox (provider_id=None)`
  Expand source code

      class cProviderInbox:
              def __init__(self, provider_id=None):
                      if provider_id is None:
                              self.__provider_id = gmStaff.gmCurrentProvider()['pk_staff']
                      else:
                              self.__provider_id = provider_id
              #--------------------------------------------------------
              def delete_message(self, pk=None):
                      return delete_inbox_message(inbox_message = pk)

              #--------------------------------------------------------
              def add_message(self, message_type=None, subject=None, patient=None):
                      return create_inbox_message (
                              message_type = message_type,
                              subject = subject,
                              patient = patient,
                              staff = self.__provider_id
                      )

              #--------------------------------------------------------
              # properties
              #--------------------------------------------------------
              def get_messages(self, pk_patient=None, include_without_provider=False, exclude_expired=False, expired_only=False, overdue_only=False, unscheduled_only=False, exclude_unscheduled=False, order_by=None):
                      return get_inbox_messages (
                              pk_staff = self.__provider_id,
                              pk_patient = pk_patient,
                              include_without_provider = include_without_provider,
                              exclude_expired = exclude_expired,
                              expired_only = expired_only,
                              overdue_only = overdue_only,
                              unscheduled_only = unscheduled_only,
                              exclude_unscheduled = exclude_unscheduled,
                              order_by = order_by
                      )

              messages = property(get_messages)

              #--------------------------------------------------------
              def get_relevant_messages(self, pk_patient=None, include_without_provider=True):
                      return get_relevant_messages (
                              pk_staff = self.__provider_id,
                              pk_patient = pk_patient,
                              include_without_provider = include_without_provider
                      )

  ### Instance variables

  `var messages`
    Expand source code

        def get_messages(self, pk_patient=None, include_without_provider=False, exclude_expired=False, expired_only=False, overdue_only=False, unscheduled_only=False, exclude_unscheduled=False, order_by=None):
                return get_inbox_messages (
                        pk_staff = self.__provider_id,
                        pk_patient = pk_patient,
                        include_without_provider = include_without_provider,
                        exclude_expired = exclude_expired,
                        expired_only = expired_only,
                        overdue_only = overdue_only,
                        unscheduled_only = unscheduled_only,
                        exclude_unscheduled = exclude_unscheduled,
                        order_by = order_by
                )

  ### Methods

  `def add_message(self, message_type=None, subject=None, patient=None)`
    Expand source code

        def add_message(self, message_type=None, subject=None, patient=None):
                return create_inbox_message (
                        message_type = message_type,
                        subject = subject,
                        patient = patient,
                        staff = self.__provider_id
                )

  `def delete_message(self, pk=None)`
    Expand source code

        def delete_message(self, pk=None):
                return delete_inbox_message(inbox_message = pk)

  `def get_messages(self, pk_patient=None, include_without_provider=False, exclude_expired=False, expired_only=False, overdue_only=False, unscheduled_only=False, exclude_unscheduled=False, order_by=None)`
    Expand source code

        def get_messages(self, pk_patient=None, include_without_provider=False, exclude_expired=False, expired_only=False, overdue_only=False, unscheduled_only=False, exclude_unscheduled=False, order_by=None):
                return get_inbox_messages (
                        pk_staff = self.__provider_id,
                        pk_patient = pk_patient,
                        include_without_provider = include_without_provider,
                        exclude_expired = exclude_expired,
                        expired_only = expired_only,
                        overdue_only = overdue_only,
                        unscheduled_only = unscheduled_only,
                        exclude_unscheduled = exclude_unscheduled,
                        order_by = order_by
                )

  `def get_relevant_messages(self, pk_patient=None, include_without_provider=True)`
    Expand source code

        def get_relevant_messages(self, pk_patient=None, include_without_provider=True):
                return get_relevant_messages (
                        pk_staff = self.__provider_id,
                        pk_patient = pk_patient,
                        include_without_provider = include_without_provider
                )