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 #--------------------------------------------------------
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 #--------------------------------------------------------
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 if gmMimeLib.convert_file ( 306 filename = filename, 307 target_mime = target_mime, 308 target_filename = target_fname 309 ): 310 return target_fname 311 312 _log.warning('conversion failed') 313 if not ignore_conversion_problems: 314 return None 315 316 if filename.endswith('.dat'): 317 if adjust_extension: 318 filename = gmMimeLib.adjust_extension_by_mimetype(filename) 319 _log.warning('programmed to ignore conversion problems, hoping receiver can handle [%s]', filename) 320 return filename
321 322 #--------------------------------------------------------
323 - def get_reviews(self):
324 cmd = """ 325 SELECT 326 reviewer, 327 reviewed_when, 328 is_technically_abnormal, 329 clinically_relevant, 330 is_review_by_responsible_reviewer, 331 is_your_review, 332 coalesce(comment, '') 333 FROM blobs.v_reviewed_doc_objects 334 WHERE pk_doc_obj = %s 335 ORDER BY 336 is_your_review desc, 337 is_review_by_responsible_reviewer desc, 338 reviewed_when desc 339 """ 340 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}]) 341 return rows
342 343 #--------------------------------------------------------
344 - def __get_containing_document(self):
345 return cDocument(aPK_obj = self._payload[self._idx['pk_doc']])
346 347 containing_document = property(__get_containing_document) 348 349 #-------------------------------------------------------- 350 # store data 351 #--------------------------------------------------------
352 - def update_data_from_file(self, fname=None, link_obj=None):
353 # sanity check 354 if not (os.access(fname, os.R_OK) and os.path.isfile(fname)): 355 _log.error('[%s] is not a readable file' % fname) 356 return False 357 358 if not gmPG2.file2bytea ( 359 conn = link_obj, 360 query = "UPDATE blobs.doc_obj SET data = %(data)s::bytea WHERE pk = %(pk)s RETURNING md5(data) AS md5", 361 filename = fname, 362 args = {'pk': self.pk_obj}, 363 file_md5 = gmTools.file2md5(filename = fname, return_hex = True) 364 ): 365 return False 366 367 # must update XMIN now ... 368 self.refetch_payload(link_obj = link_obj) 369 return True
370 371 #--------------------------------------------------------
372 - def set_reviewed(self, technically_abnormal=None, clinically_relevant=None):
373 # row already there ? 374 cmd = """ 375 select pk 376 from blobs.reviewed_doc_objs 377 where 378 fk_reviewed_row = %s and 379 fk_reviewer = (select pk from dem.staff where db_user = current_user)""" 380 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}]) 381 382 # INSERT needed 383 if len(rows) == 0: 384 cols = [ 385 "fk_reviewer", 386 "fk_reviewed_row", 387 "is_technically_abnormal", 388 "clinically_relevant" 389 ] 390 vals = [ 391 '%(fk_row)s', 392 '%(abnormal)s', 393 '%(relevant)s' 394 ] 395 args = { 396 'fk_row': self.pk_obj, 397 'abnormal': technically_abnormal, 398 'relevant': clinically_relevant 399 } 400 cmd = """ 401 insert into blobs.reviewed_doc_objs ( 402 %s 403 ) values ( 404 (select pk from dem.staff where db_user=current_user), 405 %s 406 )""" % (', '.join(cols), ', '.join(vals)) 407 408 # UPDATE needed 409 if len(rows) == 1: 410 pk_review = rows[0][0] 411 args = { 412 'abnormal': technically_abnormal, 413 'relevant': clinically_relevant, 414 'pk_review': pk_review 415 } 416 cmd = """ 417 UPDATE blobs.reviewed_doc_objs SET 418 is_technically_abnormal = %(abnormal)s, 419 clinically_relevant = %(relevant)s 420 WHERE 421 pk = %(pk_review)s 422 """ 423 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 424 425 return True
426 427 #--------------------------------------------------------
428 - def set_as_active_photograph(self):
429 if self._payload[self._idx['type']] != 'patient photograph': 430 return False 431 # set seq_idx to current max + 1 432 cmd = 'SELECT coalesce(max(seq_idx)+1, 1) FROM blobs.doc_obj WHERE fk_doc = %(doc_id)s' 433 rows, idx = gmPG2.run_ro_queries ( 434 queries = [{ 435 'cmd': cmd, 436 'args': {'doc_id': self._payload[self._idx['pk_doc']]} 437 }] 438 ) 439 self._payload[self._idx['seq_idx']] = rows[0][0] 440 self._is_modified = True 441 self.save_payload()
442 443 #--------------------------------------------------------
444 - def reattach(self, pk_doc=None):
445 if pk_doc == self._payload[self._idx['pk_doc']]: 446 return True 447 448 cmd = """ 449 UPDATE blobs.doc_obj SET 450 fk_doc = %(pk_doc_target)s, 451 -- coalesce needed for no-parts target docs 452 seq_idx = (SELECT coalesce(max(seq_idx) + 1, 1) FROM blobs.doc_obj WHERE fk_doc = %(pk_doc_target)s) 453 WHERE 454 EXISTS(SELECT 1 FROM blobs.doc_med WHERE pk = %(pk_doc_target)s) 455 AND 456 pk = %(pk_obj)s 457 AND 458 xmin = %(xmin_doc_obj)s 459 RETURNING fk_doc 460 """ 461 args = { 462 'pk_doc_target': pk_doc, 463 'pk_obj': self.pk_obj, 464 'xmin_doc_obj': self._payload[self._idx['xmin_doc_obj']] 465 } 466 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 467 if len(rows) == 0: 468 return False 469 # The following should never hold true because the target 470 # fk_doc is returned from the query and it is checked for 471 # equality before the UPDATE already. Assuming the update 472 # failed to update a row because the target fk_doc did 473 # not exist we would not get *any* rows in return - for 474 # which condition we also already checked 475 if rows[0]['fk_doc'] == self._payload[self._idx['pk_doc']]: 476 return False 477 478 self.refetch_payload() 479 return True
480 481 #--------------------------------------------------------
482 - def display_via_mime(self, chunksize=0, block=None):
483 484 fname = self.save_to_file(aChunkSize = chunksize) 485 if fname is None: 486 return False, '' 487 488 success, msg = gmMimeLib.call_viewer_on_file(fname, block = block) 489 if not success: 490 return False, msg 491 492 return True, ''
493 494 #--------------------------------------------------------
495 - def format_single_line(self):
496 f_ext = '' 497 if self._payload[self._idx['filename']] is not None: 498 f_ext = os.path.splitext(self._payload[self._idx['filename']])[1].strip('.').strip() 499 if f_ext != '': 500 f_ext = ' .' + f_ext.upper() 501 txt = _('part %s, %s%s%s of document %s from %s%s') % ( 502 self._payload[self._idx['seq_idx']], 503 gmTools.size2str(self._payload[self._idx['size']]), 504 f_ext, 505 gmTools.coalesce(self._payload[self._idx['obj_comment']], '', ' ("%s")'), 506 self._payload[self._idx['l10n_type']], 507 gmDateTime.pydt_strftime(self._payload[self._idx['date_generated']], '%Y %b %d'), 508 gmTools.coalesce(self._payload[self._idx['doc_comment']], '', ' ("%s")') 509 ) 510 return txt
511 512 #--------------------------------------------------------
513 - def format(self, single_line=False):
514 if single_line: 515 return self.format_single_line() 516 517 txt = _('%s document part [#%s]\n') % ( 518 gmTools.bool2str ( 519 boolean = self._payload[self._idx['reviewed']], 520 true_str = _('Reviewed'), 521 false_str = _('Unreviewed') 522 ), 523 self._payload[self._idx['pk_obj']] 524 ) 525 f_ext = '' 526 if self._payload[self._idx['filename']] is not None: 527 f_ext = os.path.splitext(self._payload[self._idx['filename']])[1].strip('.').strip() 528 if f_ext != '': 529 f_ext = '.' + f_ext.upper() + ' ' 530 txt += _(' Part %s: %s %s(%s Bytes)\n') % ( 531 self._payload[self._idx['seq_idx']], 532 gmTools.size2str(self._payload[self._idx['size']]), 533 f_ext, 534 self._payload[self._idx['size']] 535 ) 536 if self._payload[self._idx['filename']] is not None: 537 path, fname = os.path.split(self._payload[self._idx['filename']]) 538 if not path.endswith(os.path.sep): 539 if path != '': 540 path += os.path.sep 541 if path != '': 542 path = ' (%s)' % path 543 txt += _(' Filename: %s%s\n') % (fname, path) 544 if self._payload[self._idx['obj_comment']] is not None: 545 txt += '\n%s\n' % self._payload[self._idx['obj_comment']] 546 return txt
547 548 #--------------------------------------------------------
549 - def format_metainfo(self, callback=None):
550 """If <callback> is not None it will receive a tuple (status, description, pk_obj).""" 551 if callback is None: 552 return self.__run_metainfo_formatter() 553 554 gmWorkerThread.execute_in_worker_thread ( 555 payload_function = self.__run_metainfo_formatter, 556 completion_callback = callback, 557 worker_name = 'doc_part-metainfo_formatter-' 558 )
559 560 #--------------------------------------------------------
561 - def get_useful_filename(self, patient=None, make_unique=False, directory=None, include_gnumed_tag=True, date_before_type=False, name_first=True):
562 patient_part = '' 563 if patient is not None: 564 if name_first: 565 patient_part = '%s-' % patient.subdir_name 566 else: 567 patient_part = '-%s' % patient.subdir_name 568 569 # preserve original filename extension if available 570 suffix = '.dat' 571 if self._payload[self._idx['filename']] is not None: 572 tmp, suffix = os.path.splitext ( 573 gmTools.fname_sanitize(self._payload[self._idx['filename']]).lower() 574 ) 575 if suffix == '': 576 suffix = '.dat' 577 578 if include_gnumed_tag: 579 fname_template = 'gm_doc-part_%s-%%s' % self._payload[self._idx['seq_idx']] 580 else: 581 fname_template = '%%s-part_%s' % self._payload[self._idx['seq_idx']] 582 583 if date_before_type: 584 date_type_part = '%s-%s' % ( 585 gmDateTime.pydt_strftime(self._payload[self._idx['date_generated']], '%Y-%m-%d', 'utf-8', gmDateTime.acc_days), 586 self._payload[self._idx['l10n_type']].replace(' ', '_').replace('-', '_'), 587 ) 588 else: 589 date_type_part = '%s-%s' % ( 590 self._payload[self._idx['l10n_type']].replace(' ', '_').replace('-', '_'), 591 gmDateTime.pydt_strftime(self._payload[self._idx['date_generated']], '%Y-%m-%d', 'utf-8', gmDateTime.acc_days) 592 ) 593 594 if name_first: 595 date_type_name_part = patient_part + date_type_part 596 else: 597 date_type_name_part = date_type_part + patient_part 598 599 fname = fname_template % date_type_name_part 600 601 if make_unique: 602 fname = gmTools.get_unique_filename ( 603 prefix = '%s-' % gmTools.fname_sanitize(fname), 604 suffix = suffix, 605 tmp_dir = directory 606 ) 607 else: 608 fname = gmTools.fname_sanitize(os.path.join(gmTools.coalesce(directory, gmTools.gmPaths().tmp_dir), fname + suffix)) 609 610 return fname
611 612 useful_filename = property(get_useful_filename) 613 614 #-------------------------------------------------------- 615 # internal helpers 616 #--------------------------------------------------------
617 - def __download_to_file(self, filename=None, aChunkSize=0, conn=None):
618 if self._payload[self._idx['size']] == 0: 619 _log.debug('part size 0, nothing to download') 620 return None 621 622 if filename is None: 623 filename = gmTools.get_unique_filename() 624 success = gmPG2.bytea2file ( 625 data_query = { 626 'cmd': 'SELECT substring(data from %(start)s for %(size)s) FROM blobs.doc_obj WHERE pk=%(pk)s', 627 'args': {'pk': self.pk_obj} 628 }, 629 filename = filename, 630 chunk_size = aChunkSize, 631 data_size = self._payload[self._idx['size']], 632 conn = conn 633 ) 634 if not success: 635 return None 636 637 return filename
638 639 #--------------------------------------------------------
640 - def __run_metainfo_formatter(self):
641 filename = self.__download_to_file() 642 if filename is None: 643 _log.error('problem downloading part') 644 return (False, _('problem downloading document part')) 645 646 status, desc = gmMimeLib.describe_file(filename) 647 return (status, desc, self.pk_obj)
648 649 #------------------------------------------------------------
650 -def delete_document_part(part_pk=None, encounter_pk=None):
651 cmd = """ 652 SELECT blobs.delete_document_part(%(pk)s, %(enc)s) 653 WHERE NOT EXISTS 654 (SELECT 1 FROM clin.export_item where fk_doc_obj = %(pk)s) 655 """ 656 args = {'pk': part_pk, 'enc': encounter_pk} 657 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 658 return
659 660 #============================================================ 661 _SQL_get_document_fields = "SELECT * FROM blobs.v_doc_med b_vdm WHERE %s" 662
663 -class cDocument(gmBusinessDBObject.cBusinessDBObject):
664 """Represents one medical document.""" 665 666 _cmd_fetch_payload = _SQL_get_document_fields % "pk_doc = %s" 667 _cmds_store_payload = [ 668 """UPDATE blobs.doc_med SET 669 fk_type = %(pk_type)s, 670 fk_episode = %(pk_episode)s, 671 fk_encounter = %(pk_encounter)s, 672 fk_org_unit = %(pk_org_unit)s, 673 unit_is_receiver = %(unit_is_receiver)s, 674 clin_when = %(clin_when)s, 675 comment = gm.nullify_empty_string(%(comment)s), 676 ext_ref = gm.nullify_empty_string(%(ext_ref)s), 677 fk_hospital_stay = %(pk_hospital_stay)s 678 WHERE 679 pk = %(pk_doc)s and 680 xmin = %(xmin_doc_med)s 681 RETURNING 682 xmin AS xmin_doc_med""" 683 ] 684 _updatable_fields = [ 685 'pk_type', 686 'comment', 687 'clin_when', 688 'ext_ref', 689 'pk_episode', 690 'pk_encounter', # mainly useful when moving visual progress notes to their respective encounters 691 'pk_org_unit', 692 'unit_is_receiver', 693 'pk_hospital_stay' 694 ] 695 696 #--------------------------------------------------------
697 - def refetch_payload(self, ignore_changes=False, link_obj=None):
698 try: del self.__has_unreviewed_parts 699 except AttributeError: pass 700 701 return super(cDocument, self).refetch_payload(ignore_changes = ignore_changes, link_obj = link_obj)
702 703 #--------------------------------------------------------
704 - def get_descriptions(self, max_lng=250):
705 """Get document descriptions. 706 707 - will return a list of rows 708 """ 709 if max_lng is None: 710 cmd = "SELECT pk, text FROM blobs.doc_desc WHERE fk_doc = %s" 711 else: 712 cmd = "SELECT pk, substring(text from 1 for %s) FROM blobs.doc_desc WHERE fk_doc=%%s" % max_lng 713 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}]) 714 return rows
715 716 #--------------------------------------------------------
717 - def add_description(self, description=None):
718 cmd = "insert into blobs.doc_desc (fk_doc, text) values (%s, %s)" 719 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj, description]}]) 720 return True
721 722 #--------------------------------------------------------
723 - def update_description(self, pk=None, description=None):
724 cmd = "update blobs.doc_desc set text = %(desc)s where fk_doc = %(doc)s and pk = %(pk_desc)s" 725 gmPG2.run_rw_queries(queries = [ 726 {'cmd': cmd, 'args': {'doc': self.pk_obj, 'pk_desc': pk, 'desc': description}} 727 ]) 728 return True
729 730 #--------------------------------------------------------
731 - def delete_description(self, pk=None):
732 cmd = "delete from blobs.doc_desc where fk_doc = %(doc)s and pk = %(desc)s" 733 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'doc': self.pk_obj, 'desc': pk}}]) 734 return True
735 736 #--------------------------------------------------------
737 - def _get_parts(self):
738 cmd = _SQL_get_document_part_fields % "pk_doc = %s ORDER BY seq_idx" 739 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True) 740 return [ cDocumentPart(row = {'pk_field': 'pk_obj', 'idx': idx, 'data': r}) for r in rows ]
741 742 parts = property(_get_parts, lambda x:x) 743 744 #--------------------------------------------------------
745 - def add_part(self, file=None, link_obj=None):
746 """Add a part to the document.""" 747 # create dummy part 748 cmd = """ 749 INSERT INTO blobs.doc_obj ( 750 fk_doc, data, seq_idx 751 ) VALUES ( 752 %(doc_id)s, 753 ''::bytea, 754 (SELECT coalesce(max(seq_idx)+1, 1) FROM blobs.doc_obj WHERE fk_doc = %(doc_id)s) 755 ) RETURNING pk""" 756 rows, idx = gmPG2.run_rw_queries ( 757 link_obj = link_obj, 758 queries = [{'cmd': cmd, 'args': {'doc_id': self.pk_obj}}], 759 return_data = True 760 ) 761 # init document part instance 762 pk_part = rows[0][0] 763 new_part = cDocumentPart(aPK_obj = pk_part, link_obj = link_obj) 764 if not new_part.update_data_from_file(link_obj = link_obj, fname = file): 765 _log.error('cannot import binary data from [%s] into document part' % file) 766 gmPG2.run_rw_queries ( 767 link_obj = link_obj, 768 queries = [{'cmd': "DELETE FROM blobs.doc_obj WHERE pk = %s", 'args': [pk_part]}] 769 ) 770 return None 771 new_part['filename'] = file 772 new_part.save_payload(conn = link_obj) 773 774 return new_part
775 776 #--------------------------------------------------------
777 - def add_parts_from_files(self, files=None, reviewer=None):
778 779 new_parts = [] 780 781 for filename in files: 782 new_part = self.add_part(file = filename) 783 if new_part is None: 784 msg = 'cannot instantiate document part object from [%s]' % filename 785 _log.error(msg) 786 return (False, msg, filename) 787 new_parts.append(new_part) 788 789 if reviewer is not None: 790 new_part['pk_intended_reviewer'] = reviewer # None == Null 791 success, data = new_part.save_payload() 792 if not success: 793 msg = 'cannot set reviewer to [%s] on [%s]' % (reviewer, filename) 794 _log.error(msg) 795 _log.error(str(data)) 796 return (False, msg, filename) 797 798 return (True, '', new_parts)
799 800 #--------------------------------------------------------
801 - def save_parts_to_files(self, export_dir=None, chunksize=0, conn=None):
802 fnames = [] 803 for part in self.parts: 804 fname = part.save_to_file(aChunkSize = chunksize, directory = export_dir, conn = conn) 805 if fname is None: 806 _log.error('cannot export document part [%s]', part) 807 continue 808 fnames.append(fname) 809 return fnames
810 811 #--------------------------------------------------------
812 - def _get_has_unreviewed_parts(self):
813 try: 814 return self.__has_unreviewed_parts 815 except AttributeError: 816 pass 817 818 cmd = "SELECT EXISTS(SELECT 1 FROM blobs.v_obj4doc_no_data WHERE pk_doc = %(pk)s AND reviewed IS FALSE)" 819 args = {'pk': self.pk_obj} 820 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 821 self.__has_unreviewed_parts = rows[0][0] 822 823 return self.__has_unreviewed_parts
824 825 has_unreviewed_parts = property(_get_has_unreviewed_parts, lambda x:x) 826 827 #--------------------------------------------------------
828 - def set_reviewed(self, technically_abnormal=None, clinically_relevant=None):
829 # FIXME: this is probably inefficient 830 for part in self.parts: 831 if not part.set_reviewed(technically_abnormal, clinically_relevant): 832 return False 833 return True
834 835 #--------------------------------------------------------
836 - def set_primary_reviewer(self, reviewer=None):
837 for part in self.parts: 838 part['pk_intended_reviewer'] = reviewer 839 success, data = part.save_payload() 840 if not success: 841 _log.error('cannot set reviewer to [%s]' % reviewer) 842 _log.error(str(data)) 843 return False 844 return True
845 846 #--------------------------------------------------------
847 - def format_single_line(self):
848 849 part_count = len(self._payload[self._idx['seq_idx_list']]) 850 if part_count == 0: 851 parts = _('no parts') 852 elif part_count == 1: 853 parts = _('1 part') 854 else: 855 parts = _('%s parts') % part_count 856 857 detail = '' 858 if self._payload[self._idx['ext_ref']] is not None: 859 detail = self._payload[self._idx['ext_ref']] 860 if self._payload[self._idx['unit']] is not None: 861 template = _('%s of %s') 862 if detail == '': 863 detail = _('%s of %s') % ( 864 self._payload[self._idx['unit']], 865 self._payload[self._idx['organization']] 866 ) 867 else: 868 detail += (' @ ' + template % ( 869 self._payload[self._idx['unit']], 870 self._payload[self._idx['organization']] 871 )) 872 if detail != '': 873 detail = ' (%s)' % detail 874 875 return '%s %s (%s):%s%s' % ( 876 gmDateTime.pydt_strftime(self._payload[self._idx['clin_when']], '%Y %b %d', accuracy = gmDateTime.acc_days), 877 self._payload[self._idx['l10n_type']], 878 parts, 879 gmTools.coalesce(self._payload[self._idx['comment']], '', ' "%s"'), 880 detail 881 )
882 883 #--------------------------------------------------------
884 - def format(self, single_line=False):
885 if single_line: 886 return self.format_single_line() 887 888 part_count = len(self._payload[self._idx['seq_idx_list']]) 889 if part_count == 0: 890 parts = _('no parts') 891 elif part_count == 1: 892 parts = _('1 part') 893 else: 894 parts = _('%s parts') % part_count 895 org = '' 896 if self._payload[self._idx['unit']] is not None: 897 if self._payload[self._idx['unit_is_receiver']]: 898 org = _(' Receiver: %s @ %s\n') % ( 899 self._payload[self._idx['unit']], 900 self._payload[self._idx['organization']] 901 ) 902 else: 903 org = _(' Sender: %s @ %s\n') % ( 904 self._payload[self._idx['unit']], 905 self._payload[self._idx['organization']] 906 ) 907 stay = '' 908 if self._payload[self._idx['pk_hospital_stay']] is not None: 909 stay = _('Hospital stay') + ': %s\n' % self.hospital_stay.format ( 910 left_margin = 0, 911 include_procedures = False, 912 include_docs = False, 913 include_episode = False 914 ) 915 916 txt = _( 917 '%s (%s) #%s\n' 918 ' Created: %s\n' 919 ' Episode: %s\n' 920 '%s' 921 '%s' 922 '%s' 923 '%s' 924 '%s' 925 ) % ( 926 self._payload[self._idx['l10n_type']], 927 parts, 928 self._payload[self._idx['pk_doc']], 929 gmDateTime.pydt_strftime(self._payload[self._idx['clin_when']], format = '%Y %b %d', accuracy = gmDateTime.acc_days), 930 self._payload[self._idx['episode']], 931 gmTools.coalesce(self._payload[self._idx['health_issue']], '', _(' Health issue: %s\n')), 932 gmTools.coalesce(self._payload[self._idx['ext_ref']], '', _(' External reference: %s\n')), 933 org, 934 stay, 935 gmTools.coalesce(self._payload[self._idx['comment']], '', ' %s') 936 ) 937 938 return txt
939 940 #--------------------------------------------------------
941 - def _get_hospital_stay(self):
942 if self._payload[self._idx['pk_hospital_stay']] is None: 943 return None 944 from Gnumed.business import gmEMRStructItems 945 return gmEMRStructItems.cHospitalStay(self._payload[self._idx['pk_hospital_stay']])
946 947 hospital_stay = property(_get_hospital_stay, lambda x:x) 948 949 #--------------------------------------------------------
950 - def _get_org_unit(self):
951 if self._payload[self._idx['pk_org_unit']] is None: 952 return None 953 return gmOrganization.cOrgUnit(self._payload[self._idx['pk_org_unit']])
954 955 org_unit = property(_get_org_unit, lambda x:x) 956 957 #--------------------------------------------------------
958 - def _get_procedures(self):
959 from Gnumed.business.gmEMRStructItems import get_procedures4document 960 return get_procedures4document(pk_document = self.pk_obj)
961 962 procedures = property(_get_procedures, lambda x:x) 963 964 #--------------------------------------------------------
965 - def _get_bills(self):
966 from Gnumed.business.gmBilling import get_bills4document 967 return get_bills4document(pk_document = self.pk_obj)
968 969 bills = property(_get_bills, lambda x:x)
970 971 #------------------------------------------------------------
972 -def create_document(document_type=None, encounter=None, episode=None, link_obj=None):
973 """Returns new document instance or raises an exception.""" 974 try: 975 int(document_type) 976 cmd = """INSERT INTO blobs.doc_med (fk_type, fk_encounter, fk_episode) VALUES (%(type)s, %(enc)s, %(epi)s) RETURNING pk""" 977 except ValueError: 978 create_document_type(document_type = document_type) 979 cmd = """ 980 INSERT INTO blobs.doc_med ( 981 fk_type, 982 fk_encounter, 983 fk_episode 984 ) VALUES ( 985 coalesce ( 986 (SELECT pk from blobs.doc_type bdt where bdt.name = %(type)s), 987 (SELECT pk from blobs.doc_type bdt where _(bdt.name) = %(type)s) 988 ), 989 %(enc)s, 990 %(epi)s 991 ) RETURNING pk""" 992 args = {'type': document_type, 'enc': encounter, 'epi': episode} 993 rows, idx = gmPG2.run_rw_queries(link_obj = link_obj, queries = [{'cmd': cmd, 'args': args}], return_data = True) 994 doc = cDocument(aPK_obj = rows[0][0], link_obj = link_obj) 995 return doc
996 997 #------------------------------------------------------------
998 -def search_for_documents(patient_id=None, type_id=None, external_reference=None, pk_episode=None, pk_types=None):
999 """Searches for documents with the given patient and type ID.""" 1000 1001 if (patient_id is None) and (pk_episode is None): 1002 raise ValueError('need patient_id or pk_episode to search for document') 1003 1004 where_parts = [] 1005 args = { 1006 'pat_id': patient_id, 1007 'type_id': type_id, 1008 'ref': external_reference, 1009 'pk_epi': pk_episode 1010 } 1011 1012 if patient_id is not None: 1013 where_parts.append('pk_patient = %(pat_id)s') 1014 1015 if type_id is not None: 1016 where_parts.append('pk_type = %(type_id)s') 1017 1018 if external_reference is not None: 1019 where_parts.append('ext_ref = %(ref)s') 1020 1021 if pk_episode is not None: 1022 where_parts.append('pk_episode = %(pk_epi)s') 1023 1024 if pk_types is not None: 1025 where_parts.append('pk_type IN %(pk_types)s') 1026 args['pk_types'] = tuple(pk_types) 1027 1028 cmd = _SQL_get_document_fields % ' AND '.join(where_parts) 1029 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1030 return [ cDocument(row = {'data': r, 'idx': idx, 'pk_field': 'pk_doc'}) for r in rows ]
1031 1032 #------------------------------------------------------------
1033 -def delete_document(document_id=None, encounter_id=None):
1034 # cascades to doc_obj and doc_desc but not bill.bill 1035 cmd = "SELECT blobs.delete_document(%(pk)s, %(enc)s)" 1036 args = {'pk': document_id, 'enc': encounter_id} 1037 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 1038 if not rows[0][0]: 1039 _log.error('cannot delete document [%s]', document_id) 1040 return False 1041 return True
1042 1043 #------------------------------------------------------------
1044 -def reclassify_documents_by_type(original_type=None, target_type=None):
1045 1046 _log.debug('reclassifying documents by type') 1047 _log.debug('original: %s', original_type) 1048 _log.debug('target: %s', target_type) 1049 1050 if target_type['pk_doc_type'] == original_type['pk_doc_type']: 1051 return True 1052 1053 cmd = """ 1054 update blobs.doc_med set 1055 fk_type = %(new_type)s 1056 where 1057 fk_type = %(old_type)s 1058 """ 1059 args = {'new_type': target_type['pk_doc_type'], 'old_type': original_type['pk_doc_type']} 1060 1061 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 1062 1063 return True
1064 1065 #============================================================
1066 -class cDocumentType(gmBusinessDBObject.cBusinessDBObject):
1067 """Represents a document type.""" 1068 _cmd_fetch_payload = """select * from blobs.v_doc_type where pk_doc_type=%s""" 1069 _cmds_store_payload = [ 1070 """update blobs.doc_type set 1071 name = %(type)s 1072 where 1073 pk=%(pk_obj)s and 1074 xmin=%(xmin_doc_type)s""", 1075 """select xmin_doc_type from blobs.v_doc_type where pk_doc_type = %(pk_obj)s""" 1076 ] 1077 _updatable_fields = ['type'] 1078 #--------------------------------------------------------
1079 - def set_translation(self, translation=None):
1080 1081 if translation.strip() == '': 1082 return False 1083 1084 if translation.strip() == self._payload[self._idx['l10n_type']].strip(): 1085 return True 1086 1087 rows, idx = gmPG2.run_rw_queries ( 1088 queries = [ 1089 {'cmd': 'select i18n.i18n(%s)', 'args': [self._payload[self._idx['type']]]}, 1090 {'cmd': 'select i18n.upd_tx((select i18n.get_curr_lang()), %(orig)s, %(tx)s)', 1091 'args': { 1092 'orig': self._payload[self._idx['type']], 1093 'tx': translation 1094 } 1095 } 1096 ], 1097 return_data = True 1098 ) 1099 if not rows[0][0]: 1100 _log.error('cannot set translation to [%s]' % translation) 1101 return False 1102 1103 return self.refetch_payload()
1104 1105 #------------------------------------------------------------
1106 -def get_document_types():
1107 rows, idx = gmPG2.run_ro_queries ( 1108 queries = [{'cmd': "SELECT * FROM blobs.v_doc_type"}], 1109 get_col_idx = True 1110 ) 1111 doc_types = [] 1112 for row in rows: 1113 row_def = {'pk_field': 'pk_doc_type', 'idx': idx, 'data': row} 1114 doc_types.append(cDocumentType(row = row_def)) 1115 return doc_types
1116 1117 #------------------------------------------------------------
1118 -def get_document_type_pk(document_type=None):
1119 args = {'typ': document_type.strip()} 1120 1121 cmd = 'SELECT pk FROM blobs.doc_type WHERE name = %(typ)s' 1122 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1123 if len(rows) == 0: 1124 cmd = 'SELECT pk FROM blobs.doc_type WHERE _(name) = %(typ)s' 1125 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1126 1127 if len(rows) == 0: 1128 return None 1129 1130 return rows[0]['pk']
1131 1132 #------------------------------------------------------------
1133 -def map_types2pk(document_types=None):
1134 args = {'types': tuple(document_types)} 1135 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' 1136 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1137 return rows
1138 1139 #------------------------------------------------------------
1140 -def create_document_type(document_type=None):
1141 # check for potential dupes: 1142 cmd = 'SELECT pk FROM blobs.doc_type WHERE name = %s' 1143 rows, idx = gmPG2.run_ro_queries ( 1144 queries = [{'cmd': cmd, 'args': [document_type]}] 1145 ) 1146 if len(rows) == 0: 1147 _log.debug('creating document type [%s]', document_type) 1148 cmd1 = "INSERT INTO blobs.doc_type (name) VALUES (%s) RETURNING pk" 1149 rows, idx = gmPG2.run_rw_queries ( 1150 queries = [{'cmd': cmd1, 'args': [document_type]}], 1151 return_data = True 1152 ) 1153 return cDocumentType(aPK_obj = rows[0][0])
1154 1155 #------------------------------------------------------------
1156 -def delete_document_type(document_type=None):
1157 if document_type['is_in_use']: 1158 return False 1159 gmPG2.run_rw_queries ( 1160 queries = [{ 1161 'cmd': 'delete from blobs.doc_type where pk=%s', 1162 'args': [document_type['pk_doc_type']] 1163 }] 1164 ) 1165 return True
1166 1167 #------------------------------------------------------------
1168 -def get_ext_ref():
1169 """This needs *considerably* more smarts.""" 1170 dirname = gmTools.get_unique_filename ( 1171 prefix = '', 1172 suffix = time.strftime(".%Y%m%d-%H%M%S", time.localtime()) 1173 ) 1174 # extract name for dir 1175 path, doc_ID = os.path.split(dirname) 1176 return doc_ID
1177 1178 #============================================================ 1179 # main 1180 #------------------------------------------------------------ 1181 if __name__ == '__main__': 1182 1183 if len(sys.argv) < 2: 1184 sys.exit() 1185 1186 if sys.argv[1] != 'test': 1187 sys.exit() 1188 1189 #--------------------------------------------------------
1190 - def test_doc_types():
1191 1192 print("----------------------") 1193 print("listing document types") 1194 print("----------------------") 1195 1196 for dt in get_document_types(): 1197 print(dt) 1198 1199 print("------------------------------") 1200 print("testing document type handling") 1201 print("------------------------------") 1202 1203 dt = create_document_type(document_type = 'dummy doc type for unit test 1') 1204 print("created:", dt) 1205 1206 dt['type'] = 'dummy doc type for unit test 2' 1207 dt.save_payload() 1208 print("changed base name:", dt) 1209 1210 dt.set_translation(translation = 'Dummy-Dokumenten-Typ fuer Unit-Test') 1211 print("translated:", dt) 1212 1213 print("deleted:", delete_document_type(document_type = dt)) 1214 1215 return
1216 #--------------------------------------------------------
1217 - def test_adding_doc_part():
1218 1219 print("-----------------------") 1220 print("testing document import") 1221 print("-----------------------") 1222 1223 docs = search_for_documents(patient_id=12) 1224 doc = docs[0] 1225 print("adding to doc:", doc) 1226 1227 fname = sys.argv[1] 1228 print("adding from file:", fname) 1229 part = doc.add_part(file=fname) 1230 print("new part:", part) 1231 1232 return
1233 #--------------------------------------------------------
1234 - def test_get_documents():
1235 1236 doc_folder = cDocumentFolder(aPKey=12) 1237 1238 #photo = doc_folder.get_latest_mugshot() 1239 #print type(photo), photo 1240 1241 docs = doc_folder.get_documents() 1242 for doc in docs: 1243 #print type(doc), doc 1244 #print doc.parts 1245 #print doc.format_single_line() 1246 print('--------------------------') 1247 print(doc.format(single_line = True)) 1248 print(doc.format())
1249 1250 #--------------------------------------------------------
1251 - def test_get_useful_filename():
1252 pk = 12 1253 from Gnumed.business.gmPerson import cPatient 1254 pat = cPatient(pk) 1255 doc_folder = cDocumentFolder(aPKey = pk) 1256 for doc in doc_folder.documents: 1257 for part in doc.parts: 1258 print(part.get_useful_filename ( 1259 patient = pat, 1260 make_unique = True, 1261 directory = None, 1262 include_gnumed_tag = False, 1263 date_before_type = True, 1264 name_first = False 1265 ))
1266 1267 #--------------------------------------------------------
1268 - def test_part_metainfo_formatter():
1269 1270 #-------------------------------- 1271 def desc_printer(worker_result): 1272 status, desc = worker_result 1273 print('printer callback:') 1274 print(status) 1275 print(desc) 1276 print('<hit key> for next') 1277 return
1278 #-------------------------------- 1279 1280 pk = 12 1281 from Gnumed.business.gmPerson import cPatient 1282 pat = cPatient(pk) 1283 doc_folder = cDocumentFolder(aPKey = pk) 1284 for doc in doc_folder.documents: 1285 for part in doc.parts: 1286 part.format_metainfo(callback = desc_printer) 1287 input('waiting ...') 1288 # success, desc = part.format_metainfo() 1289 # print(success) 1290 # print(desc) 1291 # input('next') 1292 1293 # print(part.get_useful_filename ( 1294 # patient = pat, 1295 # make_unique = True, 1296 # directory = None, 1297 # include_gnumed_tag = False, 1298 # date_before_type = True, 1299 # name_first = False 1300 # )) 1301 1302 #-------------------------------------------------------- 1303 from Gnumed.pycommon import gmI18N 1304 gmI18N.activate_locale() 1305 gmI18N.install_domain() 1306 1307 #test_doc_types() 1308 #test_adding_doc_part() 1309 #test_get_documents() 1310 #test_get_useful_filename() 1311 test_part_metainfo_formatter() 1312 1313 # print get_ext_ref() 1314