Gnumed.business.gmDocuments

Package Gnumed :: Package business :: Module gmDocuments |
[frames] | no frames]

# Source Code for Module Gnumed.business.gmDocuments

       1  """This module encapsulates a document stored in a GNUmed database."""
       2  #============================================================
       3  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
       4  __license__ = "GPL v2 or later"
       5
       6  import sys, os, shutil, os.path, types, time, logging
       7
       8
       9  if __name__ == '__main__':
      10          sys.path.insert(0, '../../')
      11  from Gnumed.pycommon import gmExceptions
      12  from Gnumed.pycommon import gmBusinessDBObject
      13  from Gnumed.pycommon import gmPG2
      14  from Gnumed.pycommon import gmTools
      15  from Gnumed.pycommon import gmMimeLib
      16  from Gnumed.pycommon import gmDateTime
      17  from Gnumed.pycommon import gmWorkerThread
      18
      19  from Gnumed.business import gmOrganization
      20
      21
      22  _log = logging.getLogger('gm.docs')
      23
      24  MUGSHOT=26
      25  DOCUMENT_TYPE_VISUAL_PROGRESS_NOTE = 'visual progress note'
      26  DOCUMENT_TYPE_PRESCRIPTION = 'prescription'
      27
      28  #============================================================
      29 -class cDocumentFolder:
      30          """Represents a folder with medical documents for a single patient."""
      31
      32 -        def __init__(self, aPKey = None):
      33                  """Fails if
      34
      35                  - patient referenced by aPKey does not exist
      36                  """
      37                  self.pk_patient = aPKey                 # == identity.pk == primary key
      38                  if not self._pkey_exists():
      39                          raise gmExceptions.ConstructorError("No patient with PK [%s] in database." % aPKey)
      40
      41                  # register backend notification interests
      42                  # (keep this last so we won't hang on threads when
      43                  #  failing this constructor for other reasons ...)
      44  #               if not self._register_interests():
      45  #                       raise gmExceptions.ConstructorError, "cannot register signal interests"
      46
      47                  _log.debug('instantiated document folder for patient [%s]' % self.pk_patient)
      48          #--------------------------------------------------------
      49 -        def cleanup(self):
      50                  pass
      51          #--------------------------------------------------------
      52          # internal helper
      53          #--------------------------------------------------------
      54 -        def _pkey_exists(self):
      55                  """Does this primary key exist ?
      56
      57                  - true/false/None
      58                  """
      59                  # patient in demographic database ?
      60                  rows, idx = gmPG2.run_ro_queries(queries = [
      61                          {'cmd': "select exists(select pk from dem.identity where pk = %s)", 'args': [self.pk_patient]}
      62                  ])
      63                  if not rows[0][0]:
      64                          _log.error("patient [%s] not in demographic database" % self.pk_patient)
      65                          return None
      66                  return True
      67          #--------------------------------------------------------
      68          # API
      69          #--------------------------------------------------------
      70 -        def get_latest_freediams_prescription(self):
      71                  cmd = """
      72                          SELECT pk_doc
      73                          FROM blobs.v_doc_med
      74                          WHERE
      75                                  pk_patient = %(pat)s
      76                                          AND
      77                                  type = %(typ)s
      78                                          AND
      79                                  ext_ref = %(ref)s
      80                          ORDER BY
      81                                  clin_when DESC
      82                          LIMIT 1
      83                  """
      84                  args = {
      85                          'pat': self.pk_patient,
      86                          'typ': DOCUMENT_TYPE_PRESCRIPTION,
      87                          'ref': 'FreeDiams'
      88                  }
      89                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
      90                  if len(rows) == 0:
      91                          _log.info('no FreeDiams prescription available for patient [%s]' % self.pk_patient)
      92                          return None
      93                  prescription = cDocument(aPK_obj = rows[0][0])
      94                  return prescription
      95
      96          #--------------------------------------------------------
      97 -        def get_latest_mugshot(self):
      98                  cmd = "SELECT pk_obj FROM blobs.v_latest_mugshot WHERE pk_patient = %s"
      99                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_patient]}])
     100                  if len(rows) == 0:
     101                          _log.info('no mugshots available for patient [%s]' % self.pk_patient)
     102                          return None
     103                  return cDocumentPart(aPK_obj = rows[0][0])
     104
     105          latest_mugshot = property(get_latest_mugshot, lambda x:x)
     106
     107          #--------------------------------------------------------
     108 -        def get_mugshot_list(self, latest_only=True):
     109                  if latest_only:
     110                          cmd = "select pk_doc, pk_obj from blobs.v_latest_mugshot where pk_patient=%s"
     111                  else:
     112                          cmd = """
     113                                  select
     114                                          vdm.pk_doc as pk_doc,
     115                                          dobj.pk as pk_obj
     116                                  from
     117                                          blobs.v_doc_med vdm
     118                                          blobs.doc_obj dobj
     119                                  where
     120                                          vdm.pk_type = (select pk from blobs.doc_type where name = 'patient photograph')
     121                                          and vdm.pk_patient = %s
     122                                          and dobj.fk_doc = vdm.pk_doc
     123                          """
     124                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_patient]}])
     125                  return rows
     126
     127          #--------------------------------------------------------
     128 -        def get_doc_list(self, doc_type=None):
     129                  """return flat list of document IDs"""
     130
     131                  args = {
     132                          'ID': self.pk_patient,
     133                          'TYP': doc_type
     134                  }
     135
     136                  cmd = """
     137                          select vdm.pk_doc
     138                          from blobs.v_doc_med vdm
     139                          where
     140                                  vdm.pk_patient = %%(ID)s
     141                                  %s
     142                          order by vdm.clin_when"""
     143
     144                  if doc_type is None:
     145                          cmd = cmd % ''
     146                  else:
     147                          try:
     148                                  int(doc_type)
     149                                  cmd = cmd % 'and vdm.pk_type = %(TYP)s'
     150                          except (TypeError, ValueError):
     151                                  cmd = cmd % 'and vdm.pk_type = (select pk from blobs.doc_type where name = %(TYP)s)'
     152
     153                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
     154                  doc_ids = []
     155                  for row in rows:
     156                          doc_ids.append(row[0])
     157                  return doc_ids
     158
     159          #--------------------------------------------------------
     160 -        def get_visual_progress_notes(self, episodes=None, encounter=None):
     161                  return self.get_documents (
     162                          doc_type = DOCUMENT_TYPE_VISUAL_PROGRESS_NOTE,
     163                          pk_episodes = episodes,
     164                          encounter = encounter
     165                  )
     166
     167          #--------------------------------------------------------
     168 -        def get_unsigned_documents(self):
     169                  args = {'pat': self.pk_patient}
     170                  cmd = _SQL_get_document_fields % """
     171                          pk_doc IN (
     172                                  SELECT DISTINCT ON (b_vo.pk_doc) b_vo.pk_doc
     173                                  FROM blobs.v_obj4doc_no_data b_vo
     174                                  WHERE
     175                                          pk_patient = %(pat)s
     176                                                  AND
     177                                          reviewed IS FALSE
     178                          )
     179                          ORDER BY clin_when DESC"""
     180                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
     181                  return [ cDocument(row = {'pk_field': 'pk_doc', 'idx': idx, 'data': r}) for r in rows ]
     182
     183          #--------------------------------------------------------
     184 -        def get_documents(self, doc_type=None, pk_episodes=None, encounter=None, order_by=None, exclude_unsigned=False, pk_types=None):
     185                  """Return list of documents."""
     186
     187                  args = {
     188                          'pat': self.pk_patient,
     189                          'type': doc_type,
     190                          'enc': encounter
     191                  }
     192                  where_parts = ['pk_patient = %(pat)s']
     193
     194                  if doc_type is not None:
     195                          try:
     196                                  int(doc_type)
     197                                  where_parts.append('pk_type = %(type)s')
     198                          except (TypeError, ValueError):
     199                                  where_parts.append('pk_type = (SELECT pk FROM blobs.doc_type WHERE name = %(type)s)')
     200
     201                  if pk_types is not None:
     202                          where_parts.append('pk_type IN %(pk_types)s')
     203                          args['pk_types'] = tuple(pk_types)
     204
     205                  if (pk_episodes is not None) and (len(pk_episodes) > 0):
     206                          where_parts.append('pk_episode IN %(epis)s')
     207                          args['epis'] = tuple(pk_episodes)
     208
     209                  if encounter is not None:
     210                          where_parts.append('pk_encounter = %(enc)s')
     211
     212                  if exclude_unsigned:
     213                          where_parts.append('pk_doc IN (SELECT b_vo.pk_doc FROM blobs.v_obj4doc_no_data b_vo WHERE b_vo.pk_patient = %(pat)s AND b_vo.reviewed IS TRUE)')
     214
     215                  if order_by is None:
     216                          order_by = 'ORDER BY clin_when'
     217
     218                  cmd = "%s\n%s" % (_SQL_get_document_fields % ' AND '.join(where_parts), order_by)
     219                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
     220
     221                  return [ cDocument(row = {'pk_field': 'pk_doc', 'idx': idx, 'data': r}) for r in rows ]
     222
     223          documents = property(get_documents, lambda x:x)
     224
     225          #--------------------------------------------------------
     226 -        def add_document(self, document_type=None, encounter=None, episode=None, link_obj=None):
     227                  return create_document(link_obj = link_obj, document_type = document_type, encounter = encounter, episode = episode)
     228
     229          #--------------------------------------------------------
     230 -        def add_prescription(self, encounter=None, episode=None, link_obj=None):
     231                  return self.add_document (
     232                          link_obj = link_obj,
     233                          document_type = create_document_type (
     234                                  document_type = DOCUMENT_TYPE_PRESCRIPTION
     235                          )['pk_doc_type'],
     236                          encounter = encounter,
     237                          episode = episode
     238                  )
     239
     240          #--------------------------------------------------------
     241 -        def _get_all_document_org_units(self):
     242                  cmd = gmOrganization._SQL_get_org_unit % (
     243                          'pk_org_unit IN (SELECT DISTINCT ON (pk_org_unit) pk_org_unit FROM blobs.v_doc_med WHERE pk_patient = %(pat)s)'
     244                  )
     245                  args = {'pat': self.pk_patient}
     246                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
     247                  return [ gmOrganization.cOrgUnit(row = {'data': r, 'idx': idx, 'pk_field': 'pk_org_unit'}) for r in rows ]
     248
     249          all_document_org_units = property(_get_all_document_org_units, lambda x:x)
     250
     251  #============================================================
     252  _SQL_get_document_part_fields = "select * from blobs.v_obj4doc_no_data where %s"
     253
     254 -class cDocumentPart(gmBusinessDBObject.cBusinessDBObject):
     255          """Represents one part of a medical document."""
     256
     257          _cmd_fetch_payload = _SQL_get_document_part_fields % "pk_obj = %s"
     258          _cmds_store_payload = [
     259                  """UPDATE blobs.doc_obj SET
     260                                  seq_idx = %(seq_idx)s,
     261                                  comment = gm.nullify_empty_string(%(obj_comment)s),
     262                                  filename = gm.nullify_empty_string(%(filename)s),
     263                                  fk_intended_reviewer = %(pk_intended_reviewer)s
     264                          WHERE
     265                                  pk = %(pk_obj)s
     266                                          AND
     267                                  xmin = %(xmin_doc_obj)s
     268                          RETURNING
     269                                  xmin AS xmin_doc_obj"""
     270          ]
     271          _updatable_fields = [
     272                  'seq_idx',
     273                  'obj_comment',
     274                  'pk_intended_reviewer',
     275                  'filename'
     276          ]
     277          #--------------------------------------------------------
     278          # retrieve data
     279          #--------------------------------------------------------
     280 -        def save_to_file(self, aChunkSize=0, filename=None, target_mime=None, target_extension=None, ignore_conversion_problems=False, directory=None, adjust_extension=False, conn=None):
     281
     282                  if filename is None:
     283                          filename = self.get_useful_filename(make_unique = True, directory = directory)
     284
     285                  filename = self.__download_to_file(filename = filename)
     286                  if filename is None:
     287                          return None
     288
     289                  if target_mime is None:
     290                          if filename.endswith('.dat'):
     291                                  if adjust_extension:
     292                                          return gmMimeLib.adjust_extension_by_mimetype(filename)
     293                          return filename
     294
     295                  if target_extension is None:
     296                          target_extension = gmMimeLib.guess_ext_by_mimetype(mimetype = target_mime)
     297
     298                  target_path, name = os.path.split(filename)
     299                  name, tmp = os.path.splitext(name)
     300                  target_fname = gmTools.get_unique_filename (
     301                          prefix = '%s-conv-' % name,
     302                          suffix = target_extension
     303                  )
     304                  _log.debug('attempting conversion: [%s] -> [<%s>:%s]', filename, target_mime, target_fname)
     305                  converted_fname = gmMimeLib.convert_file (
     306                          filename = filename,
     307                          target_mime = target_mime,
     308                          target_filename = target_fname
     309                  )
     310                  if converted_fname is not None:
     311                          return converted_fname
     312
     313                  _log.warning('conversion failed')
     314                  if not ignore_conversion_problems:
     315                          return None
     316
     317                  if filename.endswith('.dat'):
     318                          if adjust_extension:
     319                                  filename = gmMimeLib.adjust_extension_by_mimetype(filename)
     320                  _log.warning('programmed to ignore conversion problems, hoping receiver can handle [%s]', filename)
     321                  return filename
     322
     323          #--------------------------------------------------------
     324 -        def get_reviews(self):
     325                  cmd = """
     326  SELECT
     327          reviewer,
     328          reviewed_when,
     329          is_technically_abnormal,
     330          clinically_relevant,
     331          is_review_by_responsible_reviewer,
     332          is_your_review,
     333          coalesce(comment, '')
     334  FROM blobs.v_reviewed_doc_objects
     335  WHERE pk_doc_obj = %s
     336  ORDER BY
     337          is_your_review desc,
     338          is_review_by_responsible_reviewer desc,
     339          reviewed_when desc
     340  """
     341                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
     342                  return rows
     343
     344          #--------------------------------------------------------
     345 -        def __get_containing_document(self):
     346                  return cDocument(aPK_obj = self._payload[self._idx['pk_doc']])
     347
     348          containing_document = property(__get_containing_document)
     349
     350          #--------------------------------------------------------
     351          # store data
     352          #--------------------------------------------------------
     353 -        def update_data_from_file(self, fname=None, link_obj=None):
     354                  # sanity check
     355                  if not (os.access(fname, os.R_OK) and os.path.isfile(fname)):
     356                          _log.error('[%s] is not a readable file' % fname)
     357                          return False
     358
     359                  cmd = "UPDATE blobs.doc_obj SET data = %(data)s::BYTEA WHERE pk = %(pk)s RETURNING md5(data) AS md5"
     360                  args = {'pk': self.pk_obj}
     361                  md5 = gmTools.file2md5(filename = fname, return_hex = True)
     362                  if not gmPG2.file2bytea(conn = link_obj, query = cmd, filename = fname, args = args, file_md5 = md5):
     363                          return False
     364
     365                  # must update XMIN now ...
     366                  self.refetch_payload(link_obj = link_obj)
     367                  return True
     368
     369          #--------------------------------------------------------
     370 -        def set_reviewed(self, technically_abnormal=None, clinically_relevant=None):
     371                  # row already there ?
     372                  cmd = """
     373  select pk
     374  from blobs.reviewed_doc_objs
     375  where
     376          fk_reviewed_row = %s and
     377          fk_reviewer = (select pk from dem.staff where db_user = current_user)"""
     378                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
     379
     380                  # INSERT needed
     381                  if len(rows) == 0:
     382                          cols = [
     383                                  "fk_reviewer",
     384                                  "fk_reviewed_row",
     385                                  "is_technically_abnormal",
     386                                  "clinically_relevant"
     387                          ]
     388                          vals = [
     389                                  '%(fk_row)s',
     390                                  '%(abnormal)s',
     391                                  '%(relevant)s'
     392                          ]
     393                          args = {
     394                                  'fk_row': self.pk_obj,
     395                                  'abnormal': technically_abnormal,
     396                                  'relevant': clinically_relevant
     397                          }
     398                          cmd = """
     399  insert into blobs.reviewed_doc_objs (
     400          %s
     401  ) values (
     402          (select pk from dem.staff where db_user=current_user),
     403          %s
     404  )""" % (', '.join(cols), ', '.join(vals))
     405
     406                  # UPDATE needed
     407                  if len(rows) == 1:
     408                          pk_review = rows[0][0]
     409                          args = {
     410                                  'abnormal': technically_abnormal,
     411                                  'relevant': clinically_relevant,
     412                                  'pk_review': pk_review
     413                          }
     414                          cmd = """
     415                                  UPDATE blobs.reviewed_doc_objs SET
     416                                          is_technically_abnormal = %(abnormal)s,
     417                                          clinically_relevant = %(relevant)s
     418                                  WHERE
     419                                          pk = %(pk_review)s
     420                          """
     421                  rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
     422
     423                  return True
     424
     425          #--------------------------------------------------------
     426 -        def set_as_active_photograph(self):
     427                  if self._payload[self._idx['type']] != 'patient photograph':
     428                          return False
     429                  # set seq_idx to current max + 1
     430                  cmd = 'SELECT coalesce(max(seq_idx)+1, 1) FROM blobs.doc_obj WHERE fk_doc = %(doc_id)s'
     431                  rows, idx = gmPG2.run_ro_queries (
     432                          queries = [{
     433                                  'cmd': cmd,
     434                                  'args': {'doc_id': self._payload[self._idx['pk_doc']]}
     435                          }]
     436                  )
     437                  self._payload[self._idx['seq_idx']] = rows[0][0]
     438                  self._is_modified = True
     439                  self.save_payload()
     440
     441          #--------------------------------------------------------
     442 -        def reattach(self, pk_doc=None):
     443                  if pk_doc == self._payload[self._idx['pk_doc']]:
     444                          return True
     445
     446                  cmd = """
     447                          UPDATE blobs.doc_obj SET
     448                                  fk_doc = %(pk_doc_target)s,
     449                                  -- coalesce needed for no-parts target docs
     450                                  seq_idx = (SELECT coalesce(max(seq_idx) + 1, 1) FROM blobs.doc_obj WHERE fk_doc = %(pk_doc_target)s)
     451                          WHERE
     452                                  EXISTS(SELECT 1 FROM blobs.doc_med WHERE pk = %(pk_doc_target)s)
     453                                          AND
     454                                  pk = %(pk_obj)s
     455                                          AND
     456                                  xmin = %(xmin_doc_obj)s
     457                          RETURNING fk_doc
     458                  """
     459                  args = {
     460                          'pk_doc_target': pk_doc,
     461                          'pk_obj': self.pk_obj,
     462                          'xmin_doc_obj': self._payload[self._idx['xmin_doc_obj']]
     463                  }
     464                  rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
     465                  if len(rows) == 0:
     466                          return False
     467                  # The following should never hold true because the target
     468                  # fk_doc is returned from the query and it is checked for
     469                  # equality before the UPDATE already. Assuming the update
     470                  # failed to update a row because the target fk_doc did
     471                  # not exist we would not get *any* rows in return - for
     472                  # which condition we also already checked
     473                  if rows[0]['fk_doc'] == self._payload[self._idx['pk_doc']]:
     474                          return False
     475
     476                  self.refetch_payload()
     477                  return True
     478
     479          #--------------------------------------------------------
     480 -        def display_via_mime(self, chunksize=0, block=None):
     481
     482                  fname = self.save_to_file(aChunkSize = chunksize)
     483                  if fname is None:
     484                          return False, ''
     485
     486                  success, msg = gmMimeLib.call_viewer_on_file(fname, block = block)
     487                  if not success:
     488                          return False, msg
     489
     490                  return True, ''
     491
     492          #--------------------------------------------------------
     493 -        def format_single_line(self):
     494                  f_ext = ''
     495                  if self._payload[self._idx['filename']] is not None:
     496                          f_ext = os.path.splitext(self._payload[self._idx['filename']])[1].strip('.').strip()
     497                  if f_ext != '':
     498                          f_ext = ' .' + f_ext.upper()
     499                  txt = _('part %s, %s%s%s of document %s from %s%s') % (
     500                          self._payload[self._idx['seq_idx']],
     501                          gmTools.size2str(self._payload[self._idx['size']]),
     502                          f_ext,
     503                          gmTools.coalesce(self._payload[self._idx['obj_comment']], '', ' ("%s")'),
     504                          self._payload[self._idx['l10n_type']],
     505                          gmDateTime.pydt_strftime(self._payload[self._idx['date_generated']], '%Y %b %d'),
     506                          gmTools.coalesce(self._payload[self._idx['doc_comment']], '', ' ("%s")')
     507                  )
     508                  return txt
     509
     510          #--------------------------------------------------------
     511 -        def format(self, single_line=False):
     512                  if single_line:
     513                          return self.format_single_line()
     514
     515                  txt = _('%s document part                 [#%s]\n') % (
     516                          gmTools.bool2str (
     517                                  boolean = self._payload[self._idx['reviewed']],
     518                                  true_str = _('Reviewed'),
     519                                  false_str = _('Unreviewed')
     520                          ),
     521                          self._payload[self._idx['pk_obj']]
     522                  )
     523                  f_ext = ''
     524                  if self._payload[self._idx['filename']] is not None:
     525                          f_ext = os.path.splitext(self._payload[self._idx['filename']])[1].strip('.').strip()
     526                  if f_ext != '':
     527                          f_ext = '.' + f_ext.upper() + ' '
     528                  txt += _(' Part %s: %s %s(%s Bytes)\n') % (
     529                          self._payload[self._idx['seq_idx']],
     530                          gmTools.size2str(self._payload[self._idx['size']]),
     531                          f_ext,
     532                          self._payload[self._idx['size']]
     533                  )
     534                  if self._payload[self._idx['filename']] is not None:
     535                          path, fname = os.path.split(self._payload[self._idx['filename']])
     536                          if not path.endswith(os.path.sep):
     537                                  if path != '':
     538                                          path += os.path.sep
     539                          if path != '':
     540                                  path = ' (%s)' % path
     541                          txt += _(' Filename: %s%s\n') % (fname, path)
     542                  if self._payload[self._idx['obj_comment']] is not None:
     543                          txt += '\n%s\n' % self._payload[self._idx['obj_comment']]
     544                  return txt
     545
     546          #--------------------------------------------------------
     547 -        def format_metainfo(self, callback=None):
     548                  """If <callback> is not None it will receive a tuple (status, description, pk_obj)."""
     549                  if callback is None:
     550                          return self.__run_metainfo_formatter()
     551
     552                  gmWorkerThread.execute_in_worker_thread (
     553                          payload_function = self.__run_metainfo_formatter,
     554                          completion_callback = callback,
     555                          worker_name = 'doc_part-metainfo_formatter-'
     556                  )
     557
     558          #--------------------------------------------------------
     559 -        def get_useful_filename(self, patient=None, make_unique=False, directory=None, include_gnumed_tag=True, date_before_type=False, name_first=True):
     560                  patient_part = ''
     561                  if patient is not None:
     562                          if name_first:
     563                                  patient_part = '%s-' % patient.subdir_name
     564                          else:
     565                                  patient_part = '-%s' % patient.subdir_name
     566
     567                  # preserve original filename extension if available
     568                  suffix = '.dat'
     569                  if self._payload[self._idx['filename']] is not None:
     570                          tmp, suffix = os.path.splitext (
     571                                  gmTools.fname_sanitize(self._payload[self._idx['filename']]).lower()
     572                          )
     573                          if suffix == '':
     574                                  suffix = '.dat'
     575
     576                  if include_gnumed_tag:
     577                          fname_template = 'gm_doc-part_%s-%%s' % self._payload[self._idx['seq_idx']]
     578                  else:
     579                          fname_template = '%%s-part_%s' % self._payload[self._idx['seq_idx']]
     580
     581                  if date_before_type:
     582                          date_type_part = '%s-%s' % (
     583                                  gmDateTime.pydt_strftime(self._payload[self._idx['date_generated']], '%Y-%m-%d', 'utf-8', gmDateTime.acc_days),
     584                                  self._payload[self._idx['l10n_type']].replace(' ', '_').replace('-', '_'),
     585                          )
     586                  else:
     587                          date_type_part = '%s-%s' % (
     588                                  self._payload[self._idx['l10n_type']].replace(' ', '_').replace('-', '_'),
     589                                  gmDateTime.pydt_strftime(self._payload[self._idx['date_generated']], '%Y-%m-%d', 'utf-8', gmDateTime.acc_days)
     590                          )
     591
     592                  if name_first:
     593                          date_type_name_part = patient_part + date_type_part
     594                  else:
     595                          date_type_name_part = date_type_part + patient_part
     596
     597                  fname = fname_template % date_type_name_part
     598
     599                  if make_unique:
     600                          fname = gmTools.get_unique_filename (
     601                                  prefix = '%s-' % gmTools.fname_sanitize(fname),
     602                                  suffix = suffix,
     603                                  tmp_dir = directory
     604                          )
     605                  else:
     606                          fname = gmTools.fname_sanitize(os.path.join(gmTools.coalesce(directory, gmTools.gmPaths().tmp_dir), fname + suffix))
     607
     608                  return fname
     609
     610          useful_filename = property(get_useful_filename)
     611
     612          #--------------------------------------------------------
     613          # internal helpers
     614          #--------------------------------------------------------
     615 -        def __download_to_file(self, filename=None, aChunkSize=0, conn=None):
     616                  if self._payload[self._idx['size']] == 0:
     617                          _log.debug('part size 0, nothing to download')
     618                          return None
     619
     620                  if filename is None:
     621                          filename = gmTools.get_unique_filename()
     622                  success = gmPG2.bytea2file (
     623                          data_query = {
     624                                  'cmd': 'SELECT substring(data from %(start)s for %(size)s) FROM blobs.doc_obj WHERE pk=%(pk)s',
     625                                  'args': {'pk': self.pk_obj}
     626                          },
     627                          filename = filename,
     628                          chunk_size = aChunkSize,
     629                          data_size = self._payload[self._idx['size']],
     630                          conn = conn
     631                  )
     632                  if not success:
     633                          return None
     634
     635                  return filename
     636
     637          #--------------------------------------------------------
     638 -        def __run_metainfo_formatter(self):
     639                  filename = self.__download_to_file()
     640                  if filename is None:
     641                          _log.error('problem downloading part')
     642                          return (False, _('problem downloading document part'))
     643
     644                  status, desc = gmMimeLib.describe_file(filename)
     645                  return (status, desc, self.pk_obj)
     646
     647  #------------------------------------------------------------
     648 -def delete_document_part(part_pk=None, encounter_pk=None):
     649          cmd = """
     650                  SELECT blobs.delete_document_part(%(pk)s, %(enc)s)
     651                  WHERE NOT EXISTS
     652                          (SELECT 1 FROM clin.export_item where fk_doc_obj = %(pk)s)
     653          """
     654          args = {'pk': part_pk, 'enc': encounter_pk}
     655          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
     656          return
     657
     658  #============================================================
     659  _SQL_get_document_fields = "SELECT * FROM blobs.v_doc_med b_vdm WHERE %s"
     660
     661 -class cDocument(gmBusinessDBObject.cBusinessDBObject):
     662          """Represents one medical document."""
     663
     664          _cmd_fetch_payload = _SQL_get_document_fields % "pk_doc = %s"
     665          _cmds_store_payload = [
     666                  """UPDATE blobs.doc_med SET
     667                                  fk_type = %(pk_type)s,
     668                                  fk_episode = %(pk_episode)s,
     669                                  fk_encounter = %(pk_encounter)s,
     670                                  fk_org_unit = %(pk_org_unit)s,
     671                                  unit_is_receiver = %(unit_is_receiver)s,
     672                                  clin_when = %(clin_when)s,
     673                                  comment = gm.nullify_empty_string(%(comment)s),
     674                                  ext_ref = gm.nullify_empty_string(%(ext_ref)s),
     675                                  fk_hospital_stay = %(pk_hospital_stay)s
     676                          WHERE
     677                                  pk = %(pk_doc)s and
     678                                  xmin = %(xmin_doc_med)s
     679                          RETURNING
     680                                  xmin AS xmin_doc_med"""
     681          ]
     682          _updatable_fields = [
     683                  'pk_type',
     684                  'comment',
     685                  'clin_when',
     686                  'ext_ref',
     687                  'pk_episode',
     688                  'pk_encounter',                 # mainly useful when moving visual progress notes to their respective encounters
     689                  'pk_org_unit',
     690                  'unit_is_receiver',
     691                  'pk_hospital_stay'
     692          ]
     693
     694          #--------------------------------------------------------
     695 -        def refetch_payload(self, ignore_changes=False, link_obj=None):
     696                  try: del self.__has_unreviewed_parts
     697                  except AttributeError: pass
     698
     699                  return super(cDocument, self).refetch_payload(ignore_changes = ignore_changes, link_obj = link_obj)
     700
     701          #--------------------------------------------------------
     702 -        def get_descriptions(self, max_lng=250):
     703                  """Get document descriptions.
     704
     705                  - will return a list of rows
     706                  """
     707                  if max_lng is None:
     708                          cmd = "SELECT pk, text FROM blobs.doc_desc WHERE fk_doc = %s"
     709                  else:
     710                          cmd = "SELECT pk, substring(text from 1 for %s) FROM blobs.doc_desc WHERE fk_doc=%%s" % max_lng
     711                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
     712                  return rows
     713
     714          #--------------------------------------------------------
     715 -        def add_description(self, description=None):
     716                  cmd = "insert into blobs.doc_desc (fk_doc, text) values (%s, %s)"
     717                  gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj, description]}])
     718                  return True
     719
     720          #--------------------------------------------------------
     721 -        def update_description(self, pk=None, description=None):
     722                  cmd = "update blobs.doc_desc set text = %(desc)s where fk_doc = %(doc)s and pk = %(pk_desc)s"
     723                  gmPG2.run_rw_queries(queries = [
     724                          {'cmd': cmd, 'args': {'doc': self.pk_obj, 'pk_desc': pk, 'desc': description}}
     725                  ])
     726                  return True
     727
     728          #--------------------------------------------------------
     729 -        def delete_description(self, pk=None):
     730                  cmd = "delete from blobs.doc_desc where fk_doc = %(doc)s and pk = %(desc)s"
     731                  gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'doc': self.pk_obj, 'desc': pk}}])
     732                  return True
     733
     734          #--------------------------------------------------------
     735 -        def _get_parts(self):
     736                  cmd = _SQL_get_document_part_fields % "pk_doc = %s ORDER BY seq_idx"
     737                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True)
     738                  return [ cDocumentPart(row = {'pk_field': 'pk_obj', 'idx': idx, 'data': r}) for r in rows ]
     739
     740          parts = property(_get_parts, lambda x:x)
     741
     742          #--------------------------------------------------------
     743 -        def add_part(self, file=None, link_obj=None):
     744                  """Add a part to the document."""
     745                  # create dummy part
     746                  cmd = """
     747                          INSERT INTO blobs.doc_obj (
     748                                  fk_doc, data, seq_idx
     749                          ) VALUES (
     750                                  %(doc_id)s,
     751                                  ''::bytea,
     752                                  (SELECT coalesce(max(seq_idx)+1, 1) FROM blobs.doc_obj WHERE fk_doc = %(doc_id)s)
     753                          ) RETURNING pk"""
     754                  rows, idx = gmPG2.run_rw_queries (
     755                          link_obj = link_obj,
     756                          queries = [{'cmd': cmd, 'args': {'doc_id': self.pk_obj}}],
     757                          return_data = True
     758                  )
     759                  # init document part instance
     760                  pk_part = rows[0][0]
     761                  new_part = cDocumentPart(aPK_obj = pk_part, link_obj = link_obj)
     762                  if not new_part.update_data_from_file(link_obj = link_obj, fname = file):
     763                          _log.error('cannot import binary data from [%s] into document part' % file)
     764                          gmPG2.run_rw_queries (
     765                                  link_obj = link_obj,
     766                                  queries = [{'cmd': "DELETE FROM blobs.doc_obj WHERE pk = %s", 'args': [pk_part]}]
     767                          )
     768                          return None
     769                  new_part['filename'] = file
     770                  new_part.save_payload(conn = link_obj)
     771
     772                  return new_part
     773
     774          #--------------------------------------------------------
     775 -        def add_parts_from_files(self, files=None, reviewer=None):
     776
     777                  new_parts = []
     778
     779                  for filename in files:
     780                          new_part = self.add_part(file = filename)
     781                          if new_part is None:
     782                                  msg = 'cannot instantiate document part object from [%s]' % filename
     783                                  _log.error(msg)
     784                                  return (False, msg, filename)
     785                          new_parts.append(new_part)
     786
     787                          if reviewer is not None:
     788                                  new_part['pk_intended_reviewer'] = reviewer                     # None == Null
     789                                  success, data = new_part.save_payload()
     790                                  if not success:
     791                                          msg = 'cannot set reviewer to [%s] on [%s]' % (reviewer, filename)
     792                                          _log.error(msg)
     793                                          _log.error(str(data))
     794                                          return (False, msg, filename)
     795
     796                  return (True, '', new_parts)
     797
     798          #--------------------------------------------------------
     799 -        def save_parts_to_files(self, export_dir=None, chunksize=0, conn=None):
     800                  fnames = []
     801                  for part in self.parts:
     802                          fname = part.save_to_file(aChunkSize = chunksize, directory = export_dir, conn = conn)
     803                          if fname is None:
     804                                  _log.error('cannot export document part [%s]', part)
     805                                  continue
     806                          fnames.append(fname)
     807                  return fnames
     808
     809          #--------------------------------------------------------
     810 -        def _get_has_unreviewed_parts(self):
     811                  try:
     812                          return self.__has_unreviewed_parts
     813                  except AttributeError:
     814                          pass
     815
     816                  cmd = "SELECT EXISTS(SELECT 1 FROM blobs.v_obj4doc_no_data WHERE pk_doc = %(pk)s AND reviewed IS FALSE)"
     817                  args = {'pk': self.pk_obj}
     818                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
     819                  self.__has_unreviewed_parts = rows[0][0]
     820
     821                  return self.__has_unreviewed_parts
     822
     823          has_unreviewed_parts = property(_get_has_unreviewed_parts, lambda x:x)
     824
     825          #--------------------------------------------------------
     826 -        def set_reviewed(self, technically_abnormal=None, clinically_relevant=None):
     827                  # FIXME: this is probably inefficient
     828                  for part in self.parts:
     829                          if not part.set_reviewed(technically_abnormal, clinically_relevant):
     830                                  return False
     831                  return True
     832
     833          #--------------------------------------------------------
     834 -        def set_primary_reviewer(self, reviewer=None):
     835                  for part in self.parts:
     836                          part['pk_intended_reviewer'] = reviewer
     837                          success, data = part.save_payload()
     838                          if not success:
     839                                  _log.error('cannot set reviewer to [%s]' % reviewer)
     840                                  _log.error(str(data))
     841                                  return False
     842                  return True
     843
     844          #--------------------------------------------------------
     845 -        def format_single_line(self):
     846
     847                  part_count = len(self._payload[self._idx['seq_idx_list']])
     848                  if part_count == 0:
     849                          parts = _('no parts')
     850                  elif part_count == 1:
     851                          parts = _('1 part')
     852                  else:
     853                          parts = _('%s parts') % part_count
     854
     855                  detail = ''
     856                  if self._payload[self._idx['ext_ref']] is not None:
     857                          detail = self._payload[self._idx['ext_ref']]
     858                  if self._payload[self._idx['unit']] is not None:
     859                          template = _('%s of %s')
     860                          if detail == '':
     861                                  detail = _('%s of %s') % (
     862                                          self._payload[self._idx['unit']],
     863                                          self._payload[self._idx['organization']]
     864                                  )
     865                          else:
     866                                  detail += (' @ ' + template % (
     867                                          self._payload[self._idx['unit']],
     868                                          self._payload[self._idx['organization']]
     869                                  ))
     870                  if detail != '':
     871                          detail = ' (%s)' % detail
     872
     873                  return '%s %s (%s):%s%s' % (
     874                          gmDateTime.pydt_strftime(self._payload[self._idx['clin_when']], '%Y %b %d', accuracy = gmDateTime.acc_days),
     875                          self._payload[self._idx['l10n_type']],
     876                          parts,
     877                          gmTools.coalesce(self._payload[self._idx['comment']], '', ' "%s"'),
     878                          detail
     879                  )
     880
     881          #--------------------------------------------------------
     882 -        def format(self, single_line=False):
     883                  if single_line:
     884                          return self.format_single_line()
     885
     886                  part_count = len(self._payload[self._idx['seq_idx_list']])
     887                  if part_count == 0:
     888                          parts = _('no parts')
     889                  elif part_count == 1:
     890                          parts = _('1 part')
     891                  else:
     892                          parts = _('%s parts') % part_count
     893                  org = ''
     894                  if self._payload[self._idx['unit']] is not None:
     895                          if self._payload[self._idx['unit_is_receiver']]:
     896                                  org = _(' Receiver: %s @ %s\n') % (
     897                                          self._payload[self._idx['unit']],
     898                                          self._payload[self._idx['organization']]
     899                                  )
     900                          else:
     901                                  org = _(' Sender: %s @ %s\n') % (
     902                                          self._payload[self._idx['unit']],
     903                                          self._payload[self._idx['organization']]
     904                                  )
     905                  stay = ''
     906                  if self._payload[self._idx['pk_hospital_stay']] is not None:
     907                          stay = _('Hospital stay') + ': %s\n' % self.hospital_stay.format (
     908                                  left_margin = 0,
     909                                  include_procedures = False,
     910                                  include_docs = False,
     911                                  include_episode = False
     912                          )
     913
     914                  txt = _(
     915                          '%s (%s)   #%s\n'
     916                          ' Created: %s\n'
     917                          ' Episode: %s\n'
     918                          '%s'
     919                          '%s'
     920                          '%s'
     921                          '%s'
     922                          '%s'
     923                  ) % (
     924                          self._payload[self._idx['l10n_type']],
     925                          parts,
     926                          self._payload[self._idx['pk_doc']],
     927                          gmDateTime.pydt_strftime(self._payload[self._idx['clin_when']], format = '%Y %b %d', accuracy = gmDateTime.acc_days),
     928                          self._payload[self._idx['episode']],
     929                          gmTools.coalesce(self._payload[self._idx['health_issue']], '', _(' Health issue: %s\n')),
     930                          gmTools.coalesce(self._payload[self._idx['ext_ref']], '', _(' External reference: %s\n')),
     931                          org,
     932                          stay,
     933                          gmTools.coalesce(self._payload[self._idx['comment']], '', ' %s')
     934                  )
     935
     936                  return txt
     937
     938          #--------------------------------------------------------
     939 -        def _get_hospital_stay(self):
     940                  if self._payload[self._idx['pk_hospital_stay']] is None:
     941                          return None
     942                  from Gnumed.business import gmEMRStructItems
     943                  return gmEMRStructItems.cHospitalStay(self._payload[self._idx['pk_hospital_stay']])
     944
     945          hospital_stay = property(_get_hospital_stay, lambda x:x)
     946
     947          #--------------------------------------------------------
     948 -        def _get_org_unit(self):
     949                  if self._payload[self._idx['pk_org_unit']] is None:
     950                          return None
     951                  return gmOrganization.cOrgUnit(self._payload[self._idx['pk_org_unit']])
     952
     953          org_unit = property(_get_org_unit, lambda x:x)
     954
     955          #--------------------------------------------------------
     956 -        def _get_procedures(self):
     957                  from Gnumed.business.gmEMRStructItems import get_procedures4document
     958                  return get_procedures4document(pk_document = self.pk_obj)
     959
     960          procedures = property(_get_procedures, lambda x:x)
     961
     962          #--------------------------------------------------------
     963 -        def _get_bills(self):
     964                  from Gnumed.business.gmBilling import get_bills4document
     965                  return get_bills4document(pk_document = self.pk_obj)
     966
     967          bills = property(_get_bills, lambda x:x)
     968
     969  #------------------------------------------------------------
     970 -def create_document(document_type=None, encounter=None, episode=None, link_obj=None):
     971          """Returns new document instance or raises an exception."""
     972          try:
     973                  int(document_type)
     974                  cmd = """INSERT INTO blobs.doc_med (fk_type, fk_encounter, fk_episode) VALUES (%(type)s, %(enc)s, %(epi)s) RETURNING pk"""
     975          except ValueError:
     976                  create_document_type(document_type = document_type)
     977                  cmd = """
     978                          INSERT INTO blobs.doc_med (
     979                                  fk_type,
     980                                  fk_encounter,
     981                                  fk_episode
     982                          ) VALUES (
     983                                  coalesce (
     984                                          (SELECT pk from blobs.doc_type bdt where bdt.name = %(type)s),
     985                                          (SELECT pk from blobs.doc_type bdt where _(bdt.name) = %(type)s)
     986                                  ),
     987                                  %(enc)s,
     988                                  %(epi)s
     989                          ) RETURNING pk"""
     990          args = {'type': document_type, 'enc': encounter, 'epi': episode}
     991          rows, idx = gmPG2.run_rw_queries(link_obj = link_obj, queries = [{'cmd': cmd, 'args': args}], return_data = True)
     992          doc = cDocument(aPK_obj = rows[0][0], link_obj = link_obj)
     993          return doc
     994
     995  #------------------------------------------------------------
     996 -def search_for_documents(patient_id=None, type_id=None, external_reference=None, pk_episode=None, pk_types=None):
     997          """Searches for documents with the given patient and type ID."""
     998
     999          if (patient_id is None) and (pk_episode is None):
    1000                  raise ValueError('need patient_id or pk_episode to search for document')
    1001
    1002          where_parts = []
    1003          args = {
    1004                  'pat_id': patient_id,
    1005                  'type_id': type_id,
    1006                  'ref': external_reference,
    1007                  'pk_epi': pk_episode
    1008          }
    1009
    1010          if patient_id is not None:
    1011                  where_parts.append('pk_patient = %(pat_id)s')
    1012
    1013          if type_id is not None:
    1014                  where_parts.append('pk_type = %(type_id)s')
    1015
    1016          if external_reference is not None:
    1017                  where_parts.append('ext_ref = %(ref)s')
    1018
    1019          if pk_episode is not None:
    1020                  where_parts.append('pk_episode = %(pk_epi)s')
    1021
    1022          if pk_types is not None:
    1023                  where_parts.append('pk_type IN %(pk_types)s')
    1024                  args['pk_types'] = tuple(pk_types)
    1025
    1026          cmd = _SQL_get_document_fields % ' AND '.join(where_parts)
    1027          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
    1028          return [ cDocument(row = {'data': r, 'idx': idx, 'pk_field': 'pk_doc'}) for r in rows ]
    1029
    1030  #------------------------------------------------------------
    1031 -def delete_document(document_id=None, encounter_id=None):
    1032          # cascades to doc_obj and doc_desc but not bill.bill
    1033          cmd = "SELECT blobs.delete_document(%(pk)s, %(enc)s)"
    1034          args = {'pk': document_id, 'enc': encounter_id}
    1035          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
    1036          if not rows[0][0]:
    1037                  _log.error('cannot delete document [%s]', document_id)
    1038                  return False
    1039          return True
    1040
    1041  #------------------------------------------------------------
    1042 -def reclassify_documents_by_type(original_type=None, target_type=None):
    1043
    1044          _log.debug('reclassifying documents by type')
    1045          _log.debug('original: %s', original_type)
    1046          _log.debug('target: %s', target_type)
    1047
    1048          if target_type['pk_doc_type'] == original_type['pk_doc_type']:
    1049                  return True
    1050
    1051          cmd = """
    1052  update blobs.doc_med set
    1053          fk_type = %(new_type)s
    1054  where
    1055          fk_type = %(old_type)s
    1056  """
    1057          args = {'new_type': target_type['pk_doc_type'], 'old_type': original_type['pk_doc_type']}
    1058
    1059          gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
    1060
    1061          return True
    1062
    1063  #============================================================
    1064 -class cDocumentType(gmBusinessDBObject.cBusinessDBObject):
    1065          """Represents a document type."""
    1066          _cmd_fetch_payload = """select * from blobs.v_doc_type where pk_doc_type=%s"""
    1067          _cmds_store_payload = [
    1068                  """update blobs.doc_type set
    1069                                  name = %(type)s
    1070                          where
    1071                                  pk=%(pk_obj)s and
    1072                                  xmin=%(xmin_doc_type)s""",
    1073                  """select xmin_doc_type from blobs.v_doc_type where pk_doc_type = %(pk_obj)s"""
    1074          ]
    1075          _updatable_fields = ['type']
    1076          #--------------------------------------------------------
    1077 -        def set_translation(self, translation=None):
    1078
    1079                  if translation.strip() == '':
    1080                          return False
    1081
    1082                  if translation.strip() == self._payload[self._idx['l10n_type']].strip():
    1083                          return True
    1084
    1085                  rows, idx = gmPG2.run_rw_queries (
    1086                          queries = [
    1087                                  {'cmd': 'select i18n.i18n(%s)', 'args': [self._payload[self._idx['type']]]},
    1088                                  {'cmd': 'select i18n.upd_tx((select i18n.get_curr_lang()), %(orig)s, %(tx)s)',
    1089                                   'args': {
    1090                                          'orig': self._payload[self._idx['type']],
    1091                                          'tx': translation
    1092                                          }
    1093                                  }
    1094                          ],
    1095                          return_data = True
    1096                  )
    1097                  if not rows[0][0]:
    1098                          _log.error('cannot set translation to [%s]' % translation)
    1099                          return False
    1100
    1101                  return self.refetch_payload()
    1102
    1103  #------------------------------------------------------------
    1104 -def get_document_types():
    1105          rows, idx = gmPG2.run_ro_queries (
    1106                  queries = [{'cmd': "SELECT * FROM blobs.v_doc_type"}],
    1107                  get_col_idx = True
    1108          )
    1109          doc_types = []
    1110          for row in rows:
    1111                  row_def = {'pk_field': 'pk_doc_type', 'idx': idx, 'data': row}
    1112                  doc_types.append(cDocumentType(row = row_def))
    1113          return doc_types
    1114
    1115  #------------------------------------------------------------
    1116 -def get_document_type_pk(document_type=None):
    1117          args = {'typ': document_type.strip()}
    1118
    1119          cmd = 'SELECT pk FROM blobs.doc_type WHERE name = %(typ)s'
    1120          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
    1121          if len(rows) == 0:
    1122                  cmd = 'SELECT pk FROM blobs.doc_type WHERE _(name) = %(typ)s'
    1123                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
    1124
    1125          if len(rows) == 0:
    1126                  return None
    1127
    1128          return rows[0]['pk']
    1129
    1130  #------------------------------------------------------------
    1131 -def map_types2pk(document_types=None):
    1132          args = {'types': tuple(document_types)}
    1133          cmd = 'SELECT pk_doc_type, coalesce(l10n_type, type) as desc FROM blobs.v_doc_type WHERE l10n_type IN %(types)s OR type IN %(types)s'
    1134          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
    1135          return rows
    1136
    1137  #------------------------------------------------------------
    1138 -def create_document_type(document_type=None):
    1139          # check for potential dupes:
    1140          cmd = 'SELECT pk FROM blobs.doc_type WHERE name = %s'
    1141          rows, idx = gmPG2.run_ro_queries (
    1142                  queries = [{'cmd': cmd, 'args': [document_type]}]
    1143          )
    1144          if len(rows) == 0:
    1145                  _log.debug('creating document type [%s]', document_type)
    1146                  cmd1 = "INSERT INTO blobs.doc_type (name) VALUES (%s) RETURNING pk"
    1147                  rows, idx = gmPG2.run_rw_queries (
    1148                          queries = [{'cmd': cmd1, 'args': [document_type]}],
    1149                          return_data = True
    1150                  )
    1151          return cDocumentType(aPK_obj = rows[0][0])
    1152
    1153  #------------------------------------------------------------
    1154 -def delete_document_type(document_type=None):
    1155          if document_type['is_in_use']:
    1156                  return False
    1157
    1158          gmPG2.run_rw_queries (
    1159                  queries = [{
    1160                          'cmd': 'delete from blobs.doc_type where pk=%s',
    1161                          'args': [document_type['pk_doc_type']]
    1162                  }]
    1163          )
    1164          return True
    1165
    1166  #------------------------------------------------------------
    1167 -def get_ext_ref():
    1168          """This needs *considerably* more smarts."""
    1169          dirname = gmTools.get_unique_filename (
    1170                  prefix = '',
    1171                  suffix = time.strftime(".%Y%m%d-%H%M%S", time.localtime())
    1172          )
    1173          # extract name for dir
    1174          path, doc_ID = os.path.split(dirname)
    1175          return doc_ID
    1176
    1177  #============================================================
    1178 -def check_mimetypes_in_archive():
    1179          mimetypes = {}
    1180          cmd = 'SELECT pk FROM blobs.doc_med'
    1181          doc_pks, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
    1182          print('Detecting mimetypes in document archive ...')
    1183          doc_idx = 0
    1184          part_count = 0
    1185          for pk_row in doc_pks:
    1186                  doc_idx += 1
    1187                  print('\n#%s - document %s of %s: ' % (pk_row['pk'], doc_idx, len(doc_pks)), end = '')
    1188                  doc = cDocument(aPK_obj = pk_row['pk'])
    1189                  for part in doc.parts:
    1190                          part_count += 1
    1191                          print('#%s:%s bytes, ' % (part['pk_obj'], part['size']), end = '')
    1192                          part_fname = part.save_to_file()
    1193                          mimetype = gmMimeLib.guess_mimetype(part_fname)
    1194                          try:
    1195                                  mimetypes[mimetype]['count'] += 1
    1196                          except KeyError:
    1197                                  mimetypes[mimetype] = {
    1198                                          'count': 1,
    1199                                          'viewer': gmMimeLib.get_viewer_cmd(mimetype),
    1200                                          'editor': gmMimeLib.get_editor_cmd(mimetype),
    1201                                          'extension': gmMimeLib.guess_ext_by_mimetype(mimetype)
    1202                                  }
    1203          print('')
    1204          print('')
    1205          print('Number of documents :', len(doc_pks))
    1206          print('Number of parts     :', part_count)
    1207          print('Number of mime types:', len(mimetypes))
    1208          for mimetype in mimetypes:
    1209                  print('')
    1210                  print('<%s>' % mimetype)
    1211                  print(' Extension:', mimetypes[mimetype]['extension'])
    1212                  print(' Use count:', mimetypes[mimetype]['count'])
    1213                  print('    Viewer:', mimetypes[mimetype]['viewer'])
    1214                  print('    Editor:', mimetypes[mimetype]['editor'])
    1215          return 0
    1216
    1217  #============================================================
    1218  # main
    1219  #------------------------------------------------------------
    1220  if __name__ == '__main__':
    1221
    1222          if len(sys.argv) < 2:
    1223                  sys.exit()
    1224
    1225          if sys.argv[1] != 'test':
    1226                  sys.exit()
    1227
    1228          #--------------------------------------------------------
    1229 -        def test_doc_types():
    1230
    1231                  print("----------------------")
    1232                  print("listing document types")
    1233                  print("----------------------")
    1234
    1235                  for dt in get_document_types():
    1236                          print(dt)
    1237
    1238                  print("------------------------------")
    1239                  print("testing document type handling")
    1240                  print("------------------------------")
    1241
    1242                  dt = create_document_type(document_type = 'dummy doc type for unit test 1')
    1243                  print("created:", dt)
    1244
    1245                  dt['type'] = 'dummy doc type for unit test 2'
    1246                  dt.save_payload()
    1247                  print("changed base name:", dt)
    1248
    1249                  dt.set_translation(translation = 'Dummy-Dokumenten-Typ fuer Unit-Test')
    1250                  print("translated:", dt)
    1251
    1252                  print("deleted:", delete_document_type(document_type = dt))
    1253
    1254                  return
    1255          #--------------------------------------------------------
    1256 -        def test_adding_doc_part():
    1257
    1258                  print("-----------------------")
    1259                  print("testing document import")
    1260                  print("-----------------------")
    1261
    1262                  docs = search_for_documents(patient_id=12)
    1263                  doc = docs[0]
    1264                  print("adding to doc:", doc)
    1265
    1266                  fname = sys.argv[1]
    1267                  print("adding from file:", fname)
    1268                  part = doc.add_part(file=fname)
    1269                  print("new part:", part)
    1270
    1271                  return
    1272          #--------------------------------------------------------
    1273 -        def test_get_documents():
    1274
    1275                  doc_folder = cDocumentFolder(aPKey=12)
    1276
    1277                  #photo = doc_folder.get_latest_mugshot()
    1278                  #print type(photo), photo
    1279
    1280                  docs = doc_folder.get_documents()
    1281                  for doc in docs:
    1282                          #print type(doc), doc
    1283                          #print doc.parts
    1284                          #print doc.format_single_line()
    1285                          print('--------------------------')
    1286                          print(doc.format(single_line = True))
    1287                          print(doc.format())
    1288
    1289          #--------------------------------------------------------
    1290 -        def test_get_useful_filename():
    1291                  pk = 12
    1292                  from Gnumed.business.gmPerson import cPatient
    1293                  pat = cPatient(pk)
    1294                  doc_folder = cDocumentFolder(aPKey = pk)
    1295                  for doc in doc_folder.documents:
    1296                          for part in doc.parts:
    1297                                  print(part.get_useful_filename (
    1298                                          patient = pat,
    1299                                          make_unique = True,
    1300                                          directory = None,
    1301                                          include_gnumed_tag = False,
    1302                                          date_before_type = True,
    1303                                          name_first = False
    1304                                  ))
    1305          #--------------------------------------------------------
    1306 -        def test_check_mimetypes_in_archive():
    1307                  check_mimetypes_in_archive()
    1308
    1309          #--------------------------------------------------------
    1310 -        def test_part_metainfo_formatter():
    1311
    1312                  #--------------------------------
    1313                  def desc_printer(worker_result):
    1314                          status, desc = worker_result
    1315                          print('printer callback:')
    1316                          print(status)
    1317                          print(desc)
    1318                          print('<hit key> for next')
    1319                          return
    1320                  #--------------------------------
    1321
    1322                  pk = 12
    1323                  from Gnumed.business.gmPerson import cPatient
    1324                  pat = cPatient(pk)
    1325                  doc_folder = cDocumentFolder(aPKey = pk)
    1326                  for doc in doc_folder.documents:
    1327                          for part in doc.parts:
    1328                                  part.format_metainfo(callback = desc_printer)
    1329                                  input('waiting ...')
    1330  #                               success, desc = part.format_metainfo()
    1331  #                               print(success)
    1332  #                               print(desc)
    1333  #                               input('next')
    1334
    1335  #                               print(part.get_useful_filename (
    1336  #                                       patient = pat,
    1337  #                                       make_unique = True,
    1338  #                                       directory = None,
    1339  #                                       include_gnumed_tag = False,
    1340  #                                       date_before_type = True,
    1341  #                                       name_first = False
    1342  #                               ))
    1343
    1344          #--------------------------------------------------------
    1345          from Gnumed.pycommon import gmI18N
    1346          gmI18N.activate_locale()
    1347          gmI18N.install_domain()
    1348
    1349          #test_doc_types()
    1350          #test_adding_doc_part()
    1351          #test_get_documents()
    1352          #test_get_useful_filename()
    1353          #test_part_metainfo_formatter()
    1354          test_check_mimetypes_in_archive()
    1355
    1356  #       print get_ext_ref()
    1357