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