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