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  @copyright: GPL v2 or later 
  4  """ 
  5  #============================================================ 
  6  __version__ = "$Revision: 1.118 $" 
  7  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
  8   
  9  import sys, os, shutil, os.path, types, time, logging 
 10   
 11   
 12  if __name__ == '__main__': 
 13          sys.path.insert(0, '../../') 
 14  from Gnumed.pycommon import gmExceptions 
 15  from Gnumed.pycommon import gmBusinessDBObject 
 16  from Gnumed.pycommon import gmPG2 
 17  from Gnumed.pycommon import gmTools 
 18  from Gnumed.pycommon import gmMimeLib 
 19  from Gnumed.pycommon import gmDateTime 
 20   
 21   
 22  _log = logging.getLogger('gm.docs') 
 23  _log.info(__version__) 
 24   
 25  MUGSHOT=26 
 26  DOCUMENT_TYPE_VISUAL_PROGRESS_NOTE = u'visual progress note' 
 27  DOCUMENT_TYPE_PRESCRIPTION = u'prescription' 
 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': u"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 #--------------------------------------------------------
71 cmd = u""" 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': u'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 - def get_latest_mugshot(self):
97 cmd = u"SELECT pk_obj FROM blobs.v_latest_mugshot WHERE pk_patient = %s" 98 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_patient]}]) 99 if len(rows) == 0: 100 _log.info('no mugshots available for patient [%s]' % self.pk_patient) 101 return None 102 return cDocumentPart(aPK_obj = rows[0][0])
103 104 latest_mugshot = property(get_latest_mugshot, lambda x:x) 105 #--------------------------------------------------------
106 - def get_mugshot_list(self, latest_only=True):
107 if latest_only: 108 cmd = u"select pk_doc, pk_obj from blobs.v_latest_mugshot where pk_patient=%s" 109 else: 110 cmd = u""" 111 select 112 vdm.pk_doc as pk_doc, 113 dobj.pk as pk_obj 114 from 115 blobs.v_doc_med vdm 116 blobs.doc_obj dobj 117 where 118 vdm.pk_type = (select pk from blobs.doc_type where name = 'patient photograph') 119 and vdm.pk_patient = %s 120 and dobj.fk_doc = vdm.pk_doc 121 """ 122 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_patient]}]) 123 return rows
124 #--------------------------------------------------------
125 - def get_doc_list(self, doc_type=None):
126 """return flat list of document IDs""" 127 128 args = { 129 'ID': self.pk_patient, 130 'TYP': doc_type 131 } 132 133 cmd = u""" 134 select vdm.pk_doc 135 from blobs.v_doc_med vdm 136 where 137 vdm.pk_patient = %%(ID)s 138 %s 139 order by vdm.clin_when""" 140 141 if doc_type is None: 142 cmd = cmd % u'' 143 else: 144 try: 145 int(doc_type) 146 cmd = cmd % u'and vdm.pk_type = %(TYP)s' 147 except (TypeError, ValueError): 148 cmd = cmd % u'and vdm.pk_type = (select pk from blobs.doc_type where name = %(TYP)s)' 149 150 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 151 doc_ids = [] 152 for row in rows: 153 doc_ids.append(row[0]) 154 return doc_ids
155 #--------------------------------------------------------
156 - def get_visual_progress_notes(self, episodes=None, encounter=None):
157 return self.get_documents ( 158 doc_type = DOCUMENT_TYPE_VISUAL_PROGRESS_NOTE, 159 episodes = episodes, 160 encounter = encounter 161 )
162 #--------------------------------------------------------
163 - def get_unsigned_documents(self):
164 args = {'pat': self.pk_patient} 165 cmd = _sql_fetch_document_fields % u""" 166 pk_doc IN ( 167 SELECT DISTINCT ON (b_vo.pk_doc) b_vo.pk_doc 168 FROM blobs.v_obj4doc_no_data b_vo 169 WHERE 170 pk_patient = %(pat)s 171 AND 172 reviewed IS FALSE 173 ) 174 ORDER BY clin_when DESC""" 175 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 176 return [ cDocument(row = {'pk_field': 'pk_doc', 'idx': idx, 'data': r}) for r in rows ]
177 #--------------------------------------------------------
178 - def get_documents(self, doc_type=None, episodes=None, encounter=None, order_by=None, exclude_unsigned=False):
179 """Return list of documents.""" 180 181 args = { 182 'pat': self.pk_patient, 183 'type': doc_type, 184 'enc': encounter 185 } 186 where_parts = [u'pk_patient = %(pat)s'] 187 188 if doc_type is not None: 189 try: 190 int(doc_type) 191 where_parts.append(u'pk_type = %(type)s') 192 except (TypeError, ValueError): 193 where_parts.append(u'pk_type = (SELECT pk FROM blobs.doc_type WHERE name = %(type)s)') 194 195 if (episodes is not None) and (len(episodes) > 0): 196 where_parts.append(u'pk_episode IN %(epi)s') 197 args['epi'] = tuple(episodes) 198 199 if encounter is not None: 200 where_parts.append(u'pk_encounter = %(enc)s') 201 202 if exclude_unsigned: 203 where_parts.append(u'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)') 204 205 if order_by is None: 206 order_by = u'ORDER BY clin_when' 207 208 cmd = u"%s\n%s" % (_sql_fetch_document_fields % u' AND '.join(where_parts), order_by) 209 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 210 211 return [ cDocument(row = {'pk_field': 'pk_doc', 'idx': idx, 'data': r}) for r in rows ]
212 213 documents = property(get_documents, lambda x:x) 214 #--------------------------------------------------------
215 - def add_document(self, document_type=None, encounter=None, episode=None):
216 return create_document(document_type = document_type, encounter = encounter, episode = episode)
217 #============================================================ 218 _sql_fetch_document_part_fields = u"select * from blobs.v_obj4doc_no_data where %s" 219
220 -class cDocumentPart(gmBusinessDBObject.cBusinessDBObject):
221 """Represents one part of a medical document.""" 222 223 _cmd_fetch_payload = _sql_fetch_document_part_fields % u"pk_obj = %s" 224 _cmds_store_payload = [ 225 u"""UPDATE blobs.doc_obj SET 226 seq_idx = %(seq_idx)s, 227 comment = gm.nullify_empty_string(%(obj_comment)s), 228 filename = gm.nullify_empty_string(%(filename)s), 229 fk_intended_reviewer = %(pk_intended_reviewer)s, 230 fk_doc = %(pk_doc)s 231 WHERE 232 pk = %(pk_obj)s 233 AND 234 xmin = %(xmin_doc_obj)s 235 RETURNING 236 xmin AS xmin_doc_obj""" 237 ] 238 _updatable_fields = [ 239 'seq_idx', 240 'obj_comment', 241 'pk_intended_reviewer', 242 'filename', 243 'pk_doc' 244 ] 245 #-------------------------------------------------------- 246 # retrieve data 247 #--------------------------------------------------------
248 - def export_to_file(self, aTempDir = None, aChunkSize = 0, filename=None):
249 250 if self._payload[self._idx['size']] == 0: 251 return None 252 253 if filename is None: 254 suffix = None 255 # preserve original filename extension if available 256 if self._payload[self._idx['filename']] is not None: 257 name, suffix = os.path.splitext(self._payload[self._idx['filename']]) 258 suffix = suffix.strip() 259 if suffix == u'': 260 suffix = None 261 # get unique filename 262 filename = gmTools.get_unique_filename ( 263 prefix = 'gm-doc_obj-page_%s-' % self._payload[self._idx['seq_idx']], 264 suffix = suffix, 265 tmp_dir = aTempDir 266 ) 267 268 success = gmPG2.bytea2file ( 269 data_query = { 270 'cmd': u'SELECT substring(data from %(start)s for %(size)s) FROM blobs.doc_obj WHERE pk=%(pk)s', 271 'args': {'pk': self.pk_obj} 272 }, 273 filename = filename, 274 chunk_size = aChunkSize, 275 data_size = self._payload[self._idx['size']] 276 ) 277 278 if success: 279 return filename 280 281 return None
282 #--------------------------------------------------------
283 - def get_reviews(self):
284 cmd = u""" 285 select 286 reviewer, 287 reviewed_when, 288 is_technically_abnormal, 289 clinically_relevant, 290 is_review_by_responsible_reviewer, 291 is_your_review, 292 coalesce(comment, '') 293 from blobs.v_reviewed_doc_objects 294 where pk_doc_obj = %s 295 order by 296 is_your_review desc, 297 is_review_by_responsible_reviewer desc, 298 reviewed_when desc 299 """ 300 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}]) 301 return rows
302 #--------------------------------------------------------
303 - def get_containing_document(self):
304 return cDocument(aPK_obj = self._payload[self._idx['pk_doc']])
305 #-------------------------------------------------------- 306 # store data 307 #--------------------------------------------------------
308 - def update_data_from_file(self, fname=None):
309 # sanity check 310 if not (os.access(fname, os.R_OK) and os.path.isfile(fname)): 311 _log.error('[%s] is not a readable file' % fname) 312 return False 313 314 gmPG2.file2bytea ( 315 query = u"UPDATE blobs.doc_obj SET data = %(data)s::bytea WHERE pk = %(pk)s", 316 filename = fname, 317 args = {'pk': self.pk_obj} 318 ) 319 320 # must update XMIN now ... 321 self.refetch_payload() 322 return True
323 #--------------------------------------------------------
324 - def set_reviewed(self, technically_abnormal=None, clinically_relevant=None):
325 # row already there ? 326 cmd = u""" 327 select pk 328 from blobs.reviewed_doc_objs 329 where 330 fk_reviewed_row = %s and 331 fk_reviewer = (select pk from dem.staff where db_user = current_user)""" 332 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}]) 333 334 # INSERT needed 335 if len(rows) == 0: 336 cols = [ 337 u"fk_reviewer", 338 u"fk_reviewed_row", 339 u"is_technically_abnormal", 340 u"clinically_relevant" 341 ] 342 vals = [ 343 u'%(fk_row)s', 344 u'%(abnormal)s', 345 u'%(relevant)s' 346 ] 347 args = { 348 'fk_row': self.pk_obj, 349 'abnormal': technically_abnormal, 350 'relevant': clinically_relevant 351 } 352 cmd = u""" 353 insert into blobs.reviewed_doc_objs ( 354 %s 355 ) values ( 356 (select pk from dem.staff where db_user=current_user), 357 %s 358 )""" % (', '.join(cols), ', '.join(vals)) 359 360 # UPDATE needed 361 if len(rows) == 1: 362 pk_row = rows[0][0] 363 args = { 364 'abnormal': technically_abnormal, 365 'relevant': clinically_relevant, 366 'pk_row': pk_row 367 } 368 cmd = u""" 369 update blobs.reviewed_doc_objs set 370 is_technically_abnormal = %(abnormal)s, 371 clinically_relevant = %(relevant)s 372 where 373 pk=%(pk_row)s""" 374 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 375 376 return True
377 #--------------------------------------------------------
378 - def set_as_active_photograph(self):
379 if self._payload[self._idx['type']] != u'patient photograph': 380 return False 381 # set seq_idx to current max + 1 382 rows, idx = gmPG2.run_ro_queries ( 383 queries = [{ 384 'cmd': u'select coalesce(max(seq_idx)+1, 1) from blobs.doc_obj where fk_doc=%(doc_id)s', 385 'args': {'doc_id': self._payload[self._idx['pk_doc']]} 386 }] 387 ) 388 self._payload[self._idx['seq_idx']] = rows[0][0] 389 self._is_modified = True 390 self.save_payload()
391 #--------------------------------------------------------
392 - def display_via_mime(self, tmpdir=None, chunksize=0, block=None):
393 394 fname = self.export_to_file(aTempDir = tmpdir, aChunkSize = chunksize) 395 if fname is None: 396 return False, '' 397 398 success, msg = gmMimeLib.call_viewer_on_file(fname, block = block) 399 if not success: 400 return False, msg 401 402 return True, ''
403 404 #------------------------------------------------------------
405 -def delete_document_part(part_pk=None, encounter_pk=None):
406 cmd = u"select blobs.delete_document_part(%(pk)s, %(enc)s)" 407 args = {'pk': part_pk, 'enc': encounter_pk} 408 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 409 return
410 #============================================================ 411 _sql_fetch_document_fields = u""" 412 SELECT 413 *, 414 COALESCE ( 415 (SELECT array_agg(seq_idx) FROM blobs.doc_obj b_do WHERE b_do.fk_doc = b_vdm.pk_doc), 416 ARRAY[]::integer[] 417 ) 418 AS seq_idx_list 419 FROM 420 blobs.v_doc_med b_vdm 421 WHERE 422 %s 423 """ 424
425 -class cDocument(gmBusinessDBObject.cBusinessDBObject):
426 """Represents one medical document.""" 427 428 _cmd_fetch_payload = _sql_fetch_document_fields % u"pk_doc = %s" 429 _cmds_store_payload = [ 430 u"""update blobs.doc_med set 431 fk_type = %(pk_type)s, 432 fk_episode = %(pk_episode)s, 433 fk_encounter = %(pk_encounter)s, 434 clin_when = %(clin_when)s, 435 comment = gm.nullify_empty_string(%(comment)s), 436 ext_ref = gm.nullify_empty_string(%(ext_ref)s) 437 where 438 pk = %(pk_doc)s and 439 xmin = %(xmin_doc_med)s""", 440 u"""select xmin_doc_med from blobs.v_doc_med where pk_doc = %(pk_doc)s""" 441 ] 442 443 _updatable_fields = [ 444 'pk_type', 445 'comment', 446 'clin_when', 447 'ext_ref', 448 'pk_episode', 449 'pk_encounter' # mainly useful when moving visual progress notes to their respective encounters 450 ] 451 #--------------------------------------------------------
452 - def refetch_payload(self, ignore_changes=False):
453 try: del self.__has_unreviewed_parts 454 except AttributeError: pass 455 456 return super(cDocument, self).refetch_payload(ignore_changes = ignore_changes)
457 #--------------------------------------------------------
458 - def get_descriptions(self, max_lng=250):
459 """Get document descriptions. 460 461 - will return a list of rows 462 """ 463 if max_lng is None: 464 cmd = u"SELECT pk, text FROM blobs.doc_desc WHERE fk_doc = %s" 465 else: 466 cmd = u"SELECT pk, substring(text from 1 for %s) FROM blobs.doc_desc WHERE fk_doc=%%s" % max_lng 467 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}]) 468 return rows
469 #--------------------------------------------------------
470 - def add_description(self, description=None):
471 cmd = u"insert into blobs.doc_desc (fk_doc, text) values (%s, %s)" 472 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj, description]}]) 473 return True
474 #--------------------------------------------------------
475 - def update_description(self, pk=None, description=None):
476 cmd = u"update blobs.doc_desc set text = %(desc)s where fk_doc = %(doc)s and pk = %(pk_desc)s" 477 gmPG2.run_rw_queries(queries = [ 478 {'cmd': cmd, 'args': {'doc': self.pk_obj, 'pk_desc': pk, 'desc': description}} 479 ]) 480 return True
481 #--------------------------------------------------------
482 - def delete_description(self, pk=None):
483 cmd = u"delete from blobs.doc_desc where fk_doc = %(doc)s and pk = %(desc)s" 484 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'doc': self.pk_obj, 'desc': pk}}]) 485 return True
486 #--------------------------------------------------------
487 - def _get_parts(self):
488 cmd = _sql_fetch_document_part_fields % u"pk_doc = %s" 489 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True) 490 return [ cDocumentPart(row = {'pk_field': 'pk_obj', 'idx': idx, 'data': r}) for r in rows ]
491 492 parts = property(_get_parts, lambda x:x) 493 #--------------------------------------------------------
494 - def add_part(self, file=None):
495 """Add a part to the document.""" 496 # create dummy part 497 cmd = u""" 498 insert into blobs.doc_obj ( 499 fk_doc, data, seq_idx 500 ) VALUES ( 501 %(doc_id)s, 502 ''::bytea, 503 (select coalesce(max(seq_idx)+1, 1) from blobs.doc_obj where fk_doc=%(doc_id)s) 504 )""" 505 rows, idx = gmPG2.run_rw_queries ( 506 queries = [ 507 {'cmd': cmd, 'args': {'doc_id': self.pk_obj}}, 508 {'cmd': u"select currval('blobs.doc_obj_pk_seq')"} 509 ], 510 return_data = True 511 ) 512 # init document part instance 513 pk_part = rows[0][0] 514 new_part = cDocumentPart(aPK_obj = pk_part) 515 if not new_part.update_data_from_file(fname=file): 516 _log.error('cannot import binary data from [%s] into document part' % file) 517 gmPG2.run_rw_queries ( 518 queries = [ 519 {'cmd': u"delete from blobs.doc_obj where pk = %s", 'args': [pk_part]} 520 ] 521 ) 522 return None 523 new_part['filename'] = file 524 new_part.save_payload() 525 526 return new_part
527 #--------------------------------------------------------
528 - def add_parts_from_files(self, files=None, reviewer=None):
529 530 new_parts = [] 531 532 for filename in files: 533 new_part = self.add_part(file = filename) 534 if new_part is None: 535 msg = 'cannot instantiate document part object' 536 _log.error(msg) 537 return (False, msg, filename) 538 new_parts.append(new_part) 539 540 if reviewer is not None: 541 new_part['pk_intended_reviewer'] = reviewer # None == Null 542 success, data = new_part.save_payload() 543 if not success: 544 msg = 'cannot set reviewer to [%s]' % reviewer 545 _log.error(msg) 546 _log.error(str(data)) 547 return (False, msg, filename) 548 549 return (True, '', new_parts)
550 #--------------------------------------------------------
551 - def export_parts_to_files(self, export_dir=None, chunksize=0):
552 fnames = [] 553 for part in self.parts: 554 # FIXME: add guess_extension_from_mimetype 555 fname = os.path.basename(gmTools.coalesce ( 556 part['filename'], 557 u'%s%s%s_%s' % (part['l10n_type'], gmTools.coalesce(part['ext_ref'], '-', '-%s-'), _('part'), part['seq_idx']) 558 )) 559 if export_dir is not None: 560 fname = os.path.join(export_dir, fname) 561 fnames.append(part.export_to_file(aChunkSize = chunksize, filename = fname)) 562 return fnames
563 #--------------------------------------------------------
565 try: 566 return self.__has_unreviewed_parts 567 except AttributeError: 568 pass 569 570 cmd = u"SELECT EXISTS(SELECT 1 FROM blobs.v_obj4doc_no_data WHERE pk_doc = %(pk)s AND reviewed IS FALSE)" 571 args = {'pk': self.pk_obj} 572 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 573 self.__has_unreviewed_parts = rows[0][0] 574 575 return self.__has_unreviewed_parts
576 577 has_unreviewed_parts = property(_get_has_unreviewed_parts, lambda x:x) 578 #--------------------------------------------------------
579 - def set_reviewed(self, technically_abnormal=None, clinically_relevant=None):
580 # FIXME: this is probably inefficient 581 for part in self.parts: 582 if not part.set_reviewed(technically_abnormal, clinically_relevant): 583 return False 584 return True
585 #--------------------------------------------------------
586 - def set_primary_reviewer(self, reviewer=None):
587 for part in self.parts: 588 part['pk_intended_reviewer'] = reviewer 589 success, data = part.save_payload() 590 if not success: 591 _log.error('cannot set reviewer to [%s]' % reviewer) 592 _log.error(str(data)) 593 return False 594 return True
595 #--------------------------------------------------------
596 - def format(self):
597 part_count = len(self._payload[self._idx['seq_idx_list']]) 598 if part_count == 1: 599 parts = _('1 part') 600 else: 601 parts = _('%s parts') % part_count 602 txt = _( 603 '%s (%s) #%s\n' 604 '\n' 605 ' Created: %s\n' 606 ' Episode: %s\n' 607 '%s' 608 '%s' 609 ) % ( 610 self._payload[self._idx['l10n_type']], 611 parts, 612 self._payload[self._idx['pk_doc']], 613 gmDateTime.pydt_strftime(self._payload[self._idx['clin_when']], format = '%Y %B %d', accuracy = gmDateTime.acc_days), 614 self._payload[self._idx['episode']], 615 gmTools.coalesce(self._payload[self._idx['ext_ref']], u'', _(' External reference: %s\n')), 616 gmTools.coalesce(self._payload[self._idx['comment']], u'', u' %s') 617 ) 618 return txt
619 #------------------------------------------------------------
620 -def create_document(document_type=None, encounter=None, episode=None):
621 """Returns new document instance or raises an exception. 622 """ 623 cmd = u"""INSERT INTO blobs.doc_med (fk_type, fk_encounter, fk_episode) VALUES (%(type)s, %(enc)s, %(epi)s) RETURNING pk""" 624 try: 625 int(document_type) 626 except ValueError: 627 cmd = u""" 628 INSERT INTO blobs.doc_med ( 629 fk_type, 630 fk_encounter, 631 fk_episode 632 ) VALUES ( 633 coalesce ( 634 (SELECT pk from blobs.doc_type bdt where bdt.name = %(type)s), 635 (SELECT pk from blobs.doc_type bdt where _(bdt.name) = %(type)s) 636 ), 637 %(enc)s, 638 %(epi)s 639 ) RETURNING pk""" 640 641 args = {'type': document_type, 'enc': encounter, 'epi': episode} 642 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 643 doc = cDocument(aPK_obj = rows[0][0]) 644 return doc
645 #------------------------------------------------------------
646 -def search_for_documents(patient_id=None, type_id=None, external_reference=None):
647 """Searches for documents with the given patient and type ID.""" 648 if patient_id is None: 649 raise ValueError('need patient id to search for document') 650 651 args = {'pat_id': patient_id, 'type_id': type_id, 'ref': external_reference} 652 where_parts = [u'pk_patient = %(pat_id)s'] 653 654 if type_id is not None: 655 where_parts.append(u'pk_type = %(type_id)s') 656 657 if external_reference is not None: 658 where_parts.append(u'ext_ref = %(ref)s') 659 660 cmd = _sql_fetch_document_fields % u' AND '.join(where_parts) 661 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 662 return [ cDocument(row = {'data': r, 'idx': idx, 'pk_field': 'pk_doc'}) for r in rows ]
663 #------------------------------------------------------------
664 -def delete_document(document_id=None, encounter_id=None):
665 # cascades to doc_obj and doc_desc but not bill.bill 666 cmd = u"SELECT blobs.delete_document(%(pk)s, %(enc)s)" 667 args = {'pk': document_id, 'enc': encounter_id} 668 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 669 if not rows[0][0]: 670 _log.error('cannot delete document [%s]', document_id) 671 return False 672 return True
673 #------------------------------------------------------------
674 -def reclassify_documents_by_type(original_type=None, target_type=None):
675 676 _log.debug('reclassifying documents by type') 677 _log.debug('original: %s', original_type) 678 _log.debug('target: %s', target_type) 679 680 if target_type['pk_doc_type'] == original_type['pk_doc_type']: 681 return True 682 683 cmd = u""" 684 update blobs.doc_med set 685 fk_type = %(new_type)s 686 where 687 fk_type = %(old_type)s 688 """ 689 args = {u'new_type': target_type['pk_doc_type'], u'old_type': original_type['pk_doc_type']} 690 691 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 692 693 return True
694 695 #============================================================
696 -class cDocumentType(gmBusinessDBObject.cBusinessDBObject):
697 """Represents a document type.""" 698 _cmd_fetch_payload = u"""select * from blobs.v_doc_type where pk_doc_type=%s""" 699 _cmds_store_payload = [ 700 u"""update blobs.doc_type set 701 name = %(type)s 702 where 703 pk=%(pk_obj)s and 704 xmin=%(xmin_doc_type)s""", 705 u"""select xmin_doc_type from blobs.v_doc_type where pk_doc_type = %(pk_obj)s""" 706 ] 707 _updatable_fields = ['type'] 708 #--------------------------------------------------------
709 - def set_translation(self, translation=None):
710 711 if translation.strip() == '': 712 return False 713 714 if translation.strip() == self._payload[self._idx['l10n_type']].strip(): 715 return True 716 717 rows, idx = gmPG2.run_rw_queries ( 718 queries = [ 719 {'cmd': u'select i18n.i18n(%s)', 'args': [self._payload[self._idx['type']]]}, 720 {'cmd': u'select i18n.upd_tx((select i18n.get_curr_lang()), %(orig)s, %(tx)s)', 721 'args': { 722 'orig': self._payload[self._idx['type']], 723 'tx': translation 724 } 725 } 726 ], 727 return_data = True 728 ) 729 if not rows[0][0]: 730 _log.error('cannot set translation to [%s]' % translation) 731 return False 732 733 return self.refetch_payload()
734 #------------------------------------------------------------
735 -def get_document_types():
736 rows, idx = gmPG2.run_ro_queries ( 737 queries = [{'cmd': u"SELECT * FROM blobs.v_doc_type"}], 738 get_col_idx = True 739 ) 740 doc_types = [] 741 for row in rows: 742 row_def = {'pk_field': 'pk_doc_type', 'idx': idx, 'data': row} 743 doc_types.append(cDocumentType(row = row_def)) 744 return doc_types
745 #------------------------------------------------------------
746 -def get_document_type_pk(document_type=None):
747 args = {'typ': document_type.strip()} 748 749 cmd = u'SELECT pk FROM blobs.doc_type WHERE name = %(typ)s' 750 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 751 if len(rows) == 0: 752 cmd = u'SELECT pk FROM blobs.doc_type WHERE _(name) = %(typ)s' 753 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 754 755 if len(rows) == 0: 756 return None 757 758 return rows[0]['pk']
759 #------------------------------------------------------------
760 -def create_document_type(document_type=None):
761 # check for potential dupes: 762 cmd = u'select pk from blobs.doc_type where name = %s' 763 rows, idx = gmPG2.run_ro_queries ( 764 queries = [{'cmd': cmd, 'args': [document_type]}] 765 ) 766 if len(rows) == 0: 767 cmd1 = u"INSERT INTO blobs.doc_type (name) VALUES (%s) RETURNING pk" 768 rows, idx = gmPG2.run_rw_queries ( 769 queries = [{'cmd': cmd1, 'args': [document_type]}], 770 return_data = True 771 ) 772 return cDocumentType(aPK_obj = rows[0][0])
773 #------------------------------------------------------------
774 -def delete_document_type(document_type=None):
775 if document_type['is_in_use']: 776 return False 777 gmPG2.run_rw_queries ( 778 queries = [{ 779 'cmd': u'delete from blobs.doc_type where pk=%s', 780 'args': [document_type['pk_doc_type']] 781 }] 782 ) 783 return True
784 #------------------------------------------------------------
785 -def get_ext_ref():
786 """This needs *considerably* more smarts.""" 787 dirname = gmTools.get_unique_filename ( 788 prefix = '', 789 suffix = time.strftime(".%Y%m%d-%H%M%S", time.localtime()) 790 ) 791 # extract name for dir 792 path, doc_ID = os.path.split(dirname) 793 return doc_ID
794 #============================================================ 795 # main 796 #------------------------------------------------------------ 797 if __name__ == '__main__': 798 799 if len(sys.argv) < 2: 800 sys.exit() 801 802 if sys.argv[1] != u'test': 803 sys.exit() 804 805 #--------------------------------------------------------
806 - def test_doc_types():
807 808 print "----------------------" 809 print "listing document types" 810 print "----------------------" 811 812 for dt in get_document_types(): 813 print dt 814 815 print "------------------------------" 816 print "testing document type handling" 817 print "------------------------------" 818 819 dt = create_document_type(document_type = 'dummy doc type for unit test 1') 820 print "created:", dt 821 822 dt['type'] = 'dummy doc type for unit test 2' 823 dt.save_payload() 824 print "changed base name:", dt 825 826 dt.set_translation(translation = 'Dummy-Dokumenten-Typ fuer Unit-Test') 827 print "translated:", dt 828 829 print "deleted:", delete_document_type(document_type = dt) 830 831 return
832 #--------------------------------------------------------
833 - def test_adding_doc_part():
834 835 print "-----------------------" 836 print "testing document import" 837 print "-----------------------" 838 839 docs = search_for_documents(patient_id=12) 840 doc = docs[0] 841 print "adding to doc:", doc 842 843 fname = sys.argv[1] 844 print "adding from file:", fname 845 part = doc.add_part(file=fname) 846 print "new part:", part 847 848 return
849 #--------------------------------------------------------
850 - def test_get_documents():
851 doc_folder = cDocumentFolder(aPKey=12) 852 853 #photo = doc_folder.get_latest_mugshot() 854 #print type(photo), photo 855 856 docs = doc_folder.get_documents() 857 for doc in docs: 858 print type(doc), doc 859 print doc.parts
860 #pprint(gmBusinessDBObject.jsonclasshintify(docs)) 861 #-------------------------------------------------------- 862 from Gnumed.pycommon import gmI18N 863 gmI18N.activate_locale() 864 gmI18N.install_domain() 865 866 #test_doc_types() 867 #test_adding_doc_part() 868 test_get_documents() 869 870 # print get_ext_ref() 871 872 #============================================================ 873