| Trees | Indices | Help |
|
|---|
|
|
1 """This module encapsulates a document stored in a GNUmed database.
2
3 @copyright: GPL
4 """
5 #============================================================
6 # $Source: /cvsroot/gnumed/gnumed/gnumed/client/business/gmMedDoc.py,v $
7 # $Id: gmMedDoc.py,v 1.118 2009/09/01 22:14:44 ncq Exp $
8 __version__ = "$Revision: 1.118 $"
9 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
10
11 import sys, os, shutil, os.path, types, time, logging
12 from cStringIO import StringIO
13
14
15 if __name__ == '__main__':
16 sys.path.insert(0, '../../')
17 from Gnumed.pycommon import gmExceptions, gmBusinessDBObject, gmPG2, gmTools, gmMimeLib
18
19
20 _log = logging.getLogger('gm.docs')
21 _log.info(__version__)
22
23 MUGSHOT=26
24 #============================================================
26 """Represents a folder with medical documents for a single patient."""
27
29 """Fails if
30
31 - patient referenced by aPKey does not exist
32 """
33 self.pk_patient = aPKey # == identity.pk == primary key
34 if not self._pkey_exists():
35 raise gmExceptions.ConstructorError, "No patient with PK [%s] in database." % aPKey
36
37 # register backend notification interests
38 # (keep this last so we won't hang on threads when
39 # failing this constructor for other reasons ...)
40 # if not self._register_interests():
41 # raise gmExceptions.ConstructorError, "cannot register signal interests"
42
43 _log.debug('instantiated document folder for patient [%s]' % self.pk_patient)
44 #--------------------------------------------------------
47 #--------------------------------------------------------
48 # internal helper
49 #--------------------------------------------------------
51 """Does this primary key exist ?
52
53 - true/false/None
54 """
55 # patient in demographic database ?
56 rows, idx = gmPG2.run_ro_queries(queries = [
57 {'cmd': u"select exists(select pk from dem.identity where pk = %s)", 'args': [self.pk_patient]}
58 ])
59 if not rows[0][0]:
60 _log.error("patient [%s] not in demographic database" % self.pk_patient)
61 return None
62 return True
63 #--------------------------------------------------------
64 # API
65 #--------------------------------------------------------
67 cmd = u"select pk_obj from blobs.v_latest_mugshot where pk_patient=%s"
68 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_patient]}])
69 if len(rows) == 0:
70 _log.info('no mugshots available for patient [%s]' % self.pk_patient)
71 return None
72 mugshot = cMedDocPart(aPK_obj=rows[0][0])
73 return mugshot
74 #--------------------------------------------------------
76 if latest_only:
77 cmd = u"select pk_doc, pk_obj from blobs.v_latest_mugshot where pk_patient=%s"
78 else:
79 cmd = u"""
80 select
81 vdm.pk_doc as pk_doc,
82 dobj.pk as pk_obj
83 from
84 blobs.v_doc_med vdm
85 blobs.doc_obj dobj
86 where
87 vdm.pk_type = (select pk from blobs.doc_type where name = 'patient photograph')
88 and vdm.pk_patient = %s
89 and dobj.fk_doc = vdm.pk_doc
90 """
91 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_patient]}])
92 return rows
93 #--------------------------------------------------------
95 """return flat list of document IDs"""
96
97 args = {
98 'ID': self.pk_patient,
99 'TYP': doc_type
100 }
101
102 cmd = u"""
103 select vdm.pk_doc
104 from blobs.v_doc_med vdm
105 where
106 vdm.pk_patient = %%(ID)s
107 %s
108 order by vdm.clin_when"""
109
110 if doc_type is None:
111 cmd = cmd % u''
112 else:
113 try:
114 int(doc_type)
115 cmd = cmd % u'and vdm.pk_type = %(TYP)s'
116 except (TypeError, ValueError):
117 cmd = cmd % u'and vdm.pk_type = (select pk from blobs.doc_type where name = %(TYP)s)'
118
119 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
120 doc_ids = []
121 for row in rows:
122 doc_ids.append(row[0])
123 return doc_ids
124 #--------------------------------------------------------
126 """Return list of documents."""
127 doc_ids = self.get_doc_list(doc_type=doc_type)
128
129 docs = []
130 for doc_id in doc_ids:
131 try:
132 docs.append(cMedDoc(aPK_obj=doc_id))
133 except gmExceptions.ConstructorError:
134 _log.exception('document error on [%s] for patient [%s]' % (doc_id, self.pk_patient))
135 continue
136
137 if episodes is not None:
138 docs = [ d for d in docs if d['pk_episode'] in episodes ]
139
140 if encounter is not None:
141 docs = [ d for d in docs if d['pk_encounter'] == encounter ]
142
143 return docs
144 #--------------------------------------------------------
146 return create_document(document_type = document_type, encounter = encounter, episode = episode)
147 #============================================================
149 """Represents one part of a medical document."""
150
151 _cmd_fetch_payload = u"""select * from blobs.v_obj4doc_no_data where pk_obj=%s"""
152 _cmds_store_payload = [
153 u"""update blobs.doc_obj set
154 seq_idx = %(seq_idx)s,
155 comment = gm.nullify_empty_string(%(obj_comment)s),
156 filename = gm.nullify_empty_string(%(filename)s),
157 fk_intended_reviewer = %(pk_intended_reviewer)s
158 where
159 pk=%(pk_obj)s and
160 xmin=%(xmin_doc_obj)s""",
161 u"""select xmin_doc_obj from blobs.v_obj4doc_no_data where pk_obj = %(pk_obj)s"""
162 ]
163 _updatable_fields = [
164 'seq_idx',
165 'obj_comment',
166 'pk_intended_reviewer',
167 'filename'
168 ]
169 #--------------------------------------------------------
170 # retrieve data
171 #--------------------------------------------------------
173
174 if self._payload[self._idx['size']] == 0:
175 return None
176
177 if filename is None:
178 suffix = None
179 # preserve original filename extension if available
180 if self._payload[self._idx['filename']] is not None:
181 name, suffix = os.path.splitext(self._payload[self._idx['filename']])
182 suffix = suffix.strip()
183 if suffix == u'':
184 suffix = None
185 # get unique filename
186 filename = gmTools.get_unique_filename (
187 prefix = 'gm-doc_obj-page_%s-' % self._payload[self._idx['seq_idx']],
188 suffix = suffix,
189 tmp_dir = aTempDir
190 )
191
192 success = gmPG2.bytea2file (
193 data_query = {
194 'cmd': u'SELECT substring(data from %(start)s for %(size)s) FROM blobs.doc_obj WHERE pk=%(pk)s',
195 'args': {'pk': self.pk_obj}
196 },
197 filename = filename,
198 chunk_size = aChunkSize,
199 data_size = self._payload[self._idx['size']]
200 )
201
202 if success:
203 return filename
204
205 return None
206 #--------------------------------------------------------
208 cmd = u"""
209 select
210 reviewer,
211 reviewed_when,
212 is_technically_abnormal,
213 clinically_relevant,
214 is_review_by_responsible_reviewer,
215 is_your_review,
216 coalesce(comment, '')
217 from blobs.v_reviewed_doc_objects
218 where pk_doc_obj = %s
219 order by
220 is_your_review desc,
221 is_review_by_responsible_reviewer desc,
222 reviewed_when desc
223 """
224 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
225 return rows
226 #--------------------------------------------------------
228 return cMedDoc(aPK_obj = self._payload[self._idx['pk_doc']])
229 #--------------------------------------------------------
230 # store data
231 #--------------------------------------------------------
233 # sanity check
234 if not (os.access(fname, os.R_OK) and os.path.isfile(fname)):
235 _log.error('[%s] is not a readable file' % fname)
236 return False
237
238 gmPG2.file2bytea (
239 query = u"UPDATE blobs.doc_obj SET data=%(data)s::bytea WHERE pk=%(pk)s",
240 filename = fname,
241 args = {'pk': self.pk_obj}
242 )
243
244 # must update XMIN now ...
245 self.refetch_payload()
246 return True
247 #--------------------------------------------------------
249 # row already there ?
250 cmd = u"""
251 select pk
252 from blobs.reviewed_doc_objs
253 where
254 fk_reviewed_row = %s and
255 fk_reviewer = (select pk from dem.staff where db_user=current_user)"""
256 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
257
258 # INSERT needed
259 if len(rows) == 0:
260 cols = [
261 u"fk_reviewer",
262 u"fk_reviewed_row",
263 u"is_technically_abnormal",
264 u"clinically_relevant"
265 ]
266 vals = [
267 u'%(fk_row)s',
268 u'%(abnormal)s',
269 u'%(relevant)s'
270 ]
271 args = {
272 'fk_row': self.pk_obj,
273 'abnormal': technically_abnormal,
274 'relevant': clinically_relevant
275 }
276 cmd = u"""
277 insert into blobs.reviewed_doc_objs (
278 %s
279 ) values (
280 (select pk from dem.staff where db_user=current_user),
281 %s
282 )""" % (', '.join(cols), ', '.join(vals))
283
284 # UPDATE needed
285 if len(rows) == 1:
286 pk_row = rows[0][0]
287 args = {
288 'abnormal': technically_abnormal,
289 'relevant': clinically_relevant,
290 'pk_row': pk_row
291 }
292 cmd = u"""
293 update blobs.reviewed_doc_objs set
294 is_technically_abnormal = %(abnormal)s,
295 clinically_relevant = %(relevant)s
296 where
297 pk=%(pk_row)s"""
298 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
299
300 return True
301 #--------------------------------------------------------
303 if self._payload[self._idx['type']] != u'patient photograph':
304 return False
305 # set seq_idx to current max + 1
306 rows, idx = gmPG2.run_ro_queries (
307 queries = [{
308 'cmd': u'select coalesce(max(seq_idx)+1, 1) from blobs.doc_obj where fk_doc=%(doc_id)s',
309 'args': {'doc_id': self._payload[self._idx['pk_doc']]}
310 }]
311 )
312 self._payload[self._idx['seq_idx']] = rows[0][0]
313 self._is_modified = True
314 self.save_payload()
315 #--------------------------------------------------------
317
318 fname = self.export_to_file(aTempDir = tmpdir, aChunkSize = chunksize)
319 if fname is None:
320 return False, ''
321
322 success, msg = gmMimeLib.call_viewer_on_file(fname, block = block)
323 if not success:
324 return False, msg
325
326 return True, ''
327 #============================================================
329 """Represents one medical document."""
330
331 _cmd_fetch_payload = u"""select * from blobs.v_doc_med where pk_doc=%s"""
332 _cmds_store_payload = [
333 u"""update blobs.doc_med set
334 fk_type = %(pk_type)s,
335 fk_episode = %(pk_episode)s,
336 clin_when = %(clin_when)s,
337 comment = gm.nullify_empty_string(%(comment)s),
338 ext_ref = gm.nullify_empty_string(%(ext_ref)s)
339 where
340 pk = %(pk_doc)s and
341 xmin = %(xmin_doc_med)s""",
342 u"""select xmin_doc_med from blobs.v_doc_med where pk_doc = %(pk_doc)s"""
343 ]
344
345 _updatable_fields = [
346 'pk_type',
347 'comment',
348 'clin_when',
349 'ext_ref',
350 'pk_episode'
351 ]
352 #--------------------------------------------------------
354 """Get document descriptions.
355
356 - will return a list of rows
357 """
358 if max_lng is None:
359 cmd = u"SELECT pk, text FROM blobs.doc_desc WHERE fk_doc = %s"
360 else:
361 cmd = u"SELECT pk, substring(text from 1 for %s) FROM blobs.doc_desc WHERE fk_doc=%%s" % max_lng
362 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
363 return rows
364 #--------------------------------------------------------
366 cmd = u"insert into blobs.doc_desc (fk_doc, text) values (%s, %s)"
367 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj, description]}])
368 return True
369 #--------------------------------------------------------
371 cmd = u"update blobs.doc_desc set text = %(desc)s where fk_doc = %(doc)s and pk = %(pk_desc)s"
372 gmPG2.run_rw_queries(queries = [
373 {'cmd': cmd, 'args': {'doc': self.pk_obj, 'pk_desc': pk, 'desc': description}}
374 ])
375 return True
376 #--------------------------------------------------------
378 cmd = u"delete from blobs.doc_desc where fk_doc = %(doc)s and pk = %(desc)s"
379 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'doc': self.pk_obj, 'desc': pk}}])
380 return True
381 #--------------------------------------------------------
383 cmd = u"select pk_obj from blobs.v_obj4doc_no_data where pk_doc=%s"
384 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
385 parts = []
386 for row in rows:
387 try:
388 parts.append(cMedDocPart(aPK_obj=row[0]))
389 except ConstructorError, msg:
390 _log.exception(msg)
391 continue
392 return parts
393 #--------------------------------------------------------
395 """Add a part to the document."""
396 # create dummy part
397 cmd = u"""
398 insert into blobs.doc_obj (
399 fk_doc, fk_intended_reviewer, data, seq_idx
400 ) VALUES (
401 %(doc_id)s,
402 (select pk_staff from dem.v_staff where db_user=CURRENT_USER),
403 ''::bytea,
404 (select coalesce(max(seq_idx)+1, 1) from blobs.doc_obj where fk_doc=%(doc_id)s)
405 )"""
406 rows, idx = gmPG2.run_rw_queries (
407 queries = [
408 {'cmd': cmd, 'args': {'doc_id': self.pk_obj}},
409 {'cmd': u"select currval('blobs.doc_obj_pk_seq')"}
410 ],
411 return_data = True
412 )
413 # init document part instance
414 pk_part = rows[0][0]
415 new_part = cMedDocPart(aPK_obj = pk_part)
416 if not new_part.update_data_from_file(fname=file):
417 _log.error('cannot import binary data from [%s] into document part' % file)
418 gmPG2.run_rw_queries (
419 queries = [
420 {'cmd': u"delete from blobs.doc_obj where pk = %s", 'args': [pk_part]}
421 ]
422 )
423 return None
424 return new_part
425 #--------------------------------------------------------
427
428 new_parts = []
429
430 for filename in files:
431 new_part = self.add_part(file=filename)
432 if new_part is None:
433 msg = 'cannot instantiate document part object'
434 _log.error(msg)
435 return (False, msg, filename)
436 new_parts.append(new_part)
437
438 new_part['filename'] = filename
439 new_part['pk_intended_reviewer'] = reviewer # None == Null
440
441 success, data = new_part.save_payload()
442 if not success:
443 msg = 'cannot set reviewer to [%s]' % reviewer
444 _log.error(msg)
445 _log.error(str(data))
446 return (False, msg, filename)
447
448 return (True, '', new_parts)
449 #--------------------------------------------------------
451 fnames = []
452 for part in self.get_parts():
453 # FIXME: add guess_extension_from_mimetype
454 fname = os.path.basename(gmTools.coalesce (
455 part['filename'],
456 u'%s%s%s_%s' % (part['l10n_type'], gmTools.coalesce(part['ext_ref'], '-', '-%s-'), _('part'), part['seq_idx'])
457 ))
458 if export_dir is not None:
459 fname = os.path.join(export_dir, fname)
460 fnames.append(part.export_to_file(aChunkSize = chunksize, filename = fname))
461 return fnames
462 #--------------------------------------------------------
464 cmd = u"select exists(select 1 from blobs.v_obj4doc_no_data where pk_doc=%s and not reviewed)"
465 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
466 return rows[0][0]
467 #--------------------------------------------------------
469 # FIXME: this is probably inefficient
470 for part in self.get_parts():
471 if not part.set_reviewed(technically_abnormal, clinically_relevant):
472 return False
473 return True
474 #--------------------------------------------------------
484
485 #------------------------------------------------------------
487 """Returns new document instance or raises an exception.
488 """
489 cmd1 = u"""insert into blobs.doc_med (fk_type, fk_encounter, fk_episode) VALUES (%(type)s, %(enc)s, %(epi)s)"""
490 cmd2 = u"""select currval('blobs.doc_med_pk_seq')"""
491 rows, idx = gmPG2.run_rw_queries (
492 queries = [
493 {'cmd': cmd1, 'args': {'type': document_type, 'enc': encounter, 'epi': episode}},
494 {'cmd': cmd2}
495 ],
496 return_data = True
497 )
498 doc_id = rows[0][0]
499 doc = cMedDoc(aPK_obj = doc_id)
500 return doc
501 #------------------------------------------------------------
503 """Searches for documents with the given patient and type ID.
504
505 No type ID returns all documents for the patient.
506 """
507 # sanity checks
508 if patient_id is None:
509 raise ValueError('need patient id to search for document')
510
511 args = {'pat_id': patient_id, 'type_id': type_id}
512 if type_id is None:
513 cmd = u"SELECT pk_doc from blobs.v_doc_med WHERE pk_patient = %(pat_id)s"
514 else:
515 cmd = u"SELECT pk_doc from blobs.v_doc_med WHERE pk_patient = %(pat_id)s and pk_type = %(type_id)s"
516
517 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
518
519 docs = []
520 for row in rows:
521 docs.append(cMedDoc(row[0]))
522 return docs
523 #------------------------------------------------------------
525 # will cascade to doc_obj and doc_desc
526 cmd = u"select blobs.delete_document(%(pk)s, %(enc)s)"
527 args = {'pk': document_id, 'enc': encounter_id}
528 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
529 return
530 #------------------------------------------------------------
532
533 _log.debug('reclassifying documents by type')
534 _log.debug('original: %s', original_type)
535 _log.debug('target: %s', target_type)
536
537 if target_type['pk_doc_type'] == original_type['pk_doc_type']:
538 return True
539
540 cmd = u"""
541 update blobs.doc_med set
542 fk_type = %(new_type)s
543 where
544 fk_type = %(old_type)s
545 """
546 args = {u'new_type': target_type['pk_doc_type'], u'old_type': original_type['pk_doc_type']}
547
548 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
549
550 return True
551
552 #============================================================
554 """Represents a document type."""
555 _cmd_fetch_payload = u"""select * from blobs.v_doc_type where pk_doc_type=%s"""
556 _cmds_store_payload = [
557 u"""update blobs.doc_type set
558 name = %(type)s
559 where
560 pk=%(pk_obj)s and
561 xmin=%(xmin_doc_type)s""",
562 u"""select xmin_doc_type from blobs.v_doc_type where pk_doc_type = %(pk_obj)s"""
563 ]
564 _updatable_fields = ['type']
565 #--------------------------------------------------------
567
568 if translation.strip() == '':
569 return False
570
571 if translation.strip() == self._payload[self._idx['l10n_type']].strip():
572 return True
573
574 rows, idx = gmPG2.run_rw_queries (
575 queries = [
576 {'cmd': u'select i18n.i18n(%s)', 'args': [self._payload[self._idx['type']]]},
577 {'cmd': u'select i18n.upd_tx((select i18n.get_curr_lang()), %(orig)s, %(tx)s)',
578 'args': {
579 'orig': self._payload[self._idx['type']],
580 'tx': translation
581 }
582 }
583 ],
584 return_data = True
585 )
586 if not rows[0][0]:
587 _log.error('cannot set translation to [%s]' % translation)
588 return False
589
590 return self.refetch_payload()
591
592 #------------------------------------------------------------
594 rows, idx = gmPG2.run_ro_queries (
595 queries = [{'cmd': u"SELECT * FROM blobs.v_doc_type"}],
596 get_col_idx = True
597 )
598 doc_types = []
599 for row in rows:
600 row_def = {
601 'pk_field': 'pk_doc_type',
602 'idx': idx,
603 'data': row
604 }
605 doc_types.append(cDocumentType(row = row_def))
606 return doc_types
607 #------------------------------------------------------------
609 # check for potential dupes:
610 cmd = u'select pk from blobs.doc_type where name = %s'
611 rows, idx = gmPG2.run_ro_queries (
612 queries = [{'cmd': cmd, 'args': [document_type]}]
613 )
614 if len(rows) == 0:
615 cmd1 = u"insert into blobs.doc_type (name) values (%s)"
616 cmd2 = u"select currval('blobs.doc_type_pk_seq')"
617 rows, idx = gmPG2.run_rw_queries (
618 queries = [
619 {'cmd': cmd1, 'args': [document_type]},
620 {'cmd': cmd2}
621 ],
622 return_data = True
623 )
624 return cDocumentType(aPK_obj = rows[0][0])
625 #------------------------------------------------------------
627 if document_type['is_in_use']:
628 return False
629 gmPG2.run_rw_queries (
630 queries = [{
631 'cmd': u'delete from blobs.doc_type where pk=%s',
632 'args': [document_type['pk_doc_type']]
633 }]
634 )
635 return True
636 #------------------------------------------------------------
638 """This needs *considerably* more smarts."""
639 dirname = gmTools.get_unique_filename (
640 prefix = '',
641 suffix = time.strftime(".%Y%m%d-%H%M%S", time.localtime())
642 )
643 # extract name for dir
644 path, doc_ID = os.path.split(dirname)
645 return doc_ID
646 #============================================================
647 # main
648 #------------------------------------------------------------
649 if __name__ == '__main__':
650
651 #--------------------------------------------------------
653
654 print "----------------------"
655 print "listing document types"
656 print "----------------------"
657
658 for dt in get_document_types():
659 print dt
660
661 print "------------------------------"
662 print "testing document type handling"
663 print "------------------------------"
664
665 dt = create_document_type(document_type = 'dummy doc type for unit test 1')
666 print "created:", dt
667
668 dt['type'] = 'dummy doc type for unit test 2'
669 dt.save_payload()
670 print "changed base name:", dt
671
672 dt.set_translation(translation = 'Dummy-Dokumenten-Typ fuer Unit-Test')
673 print "translated:", dt
674
675 print "deleted:", delete_document_type(document_type = dt)
676
677 return
678 #--------------------------------------------------------
680
681 print "-----------------------"
682 print "testing document import"
683 print "-----------------------"
684
685 docs = search_for_document(patient_id=12)
686 doc = docs[0]
687 print "adding to doc:", doc
688
689 fname = sys.argv[1]
690 print "adding from file:", fname
691 part = doc.add_part(file=fname)
692 print "new part:", part
693
694 return
695 #--------------------------------------------------------
696 from Gnumed.pycommon import gmI18N
697 gmI18N.activate_locale()
698 gmI18N.install_domain()
699
700 test_doc_types()
701 test_adding_doc_part()
702
703 # print get_ext_ref()
704
705 # doc_folder = cDocumentFolder(aPKey=12)
706
707 # photo = doc_folder.get_latest_mugshot()
708 # print type(photo), photo
709
710 # docs = doc_folder.get_documents()
711 # for doc in docs:
712 # print type(doc), doc
713
714 #============================================================
715 # $Log: gmMedDoc.py,v $
716 # Revision 1.118 2009/09/01 22:14:44 ncq
717 # - nullify empty strings where useful
718 #
719 # Revision 1.117 2009/07/18 14:06:14 ncq
720 # - fix getting doc IDs list with doc type specified
721 #
722 # Revision 1.116 2009/07/16 11:30:38 ncq
723 # - cleanup
724 #
725 # Revision 1.115 2009/07/15 12:15:04 ncq
726 # - add missing ) in setting trans for doc type
727 #
728 # Revision 1.114 2009/06/04 16:23:40 ncq
729 # - fix fk_type that needs to be pk_type
730 #
731 # Revision 1.113 2009/05/13 13:10:31 ncq
732 # - sort doc retrieval by clin_when
733 #
734 # Revision 1.112 2009/02/18 13:43:38 ncq
735 # - get_unique_filename API change
736 #
737 # Revision 1.111 2009/01/15 11:31:58 ncq
738 # - implement description handling
739 #
740 # Revision 1.110 2009/01/11 19:16:05 ncq
741 # - pseudo method
742 #
743 # Revision 1.109 2009/01/08 16:42:01 ncq
744 # - get_descriptions now includes pks of rows
745 #
746 # Revision 1.108 2008/12/09 23:21:54 ncq
747 # - no more fk_identity in doc_med
748 # - date -> clin_when in doc_med
749 #
750 # Revision 1.107 2008/11/20 18:41:36 ncq
751 # - rename arg in get_documents
752 #
753 # Revision 1.106 2008/10/12 15:14:14 ncq
754 # - use i18n.get_curr_lang
755 #
756 # Revision 1.105 2008/06/26 21:19:15 ncq
757 # - enhance get_documents with episode/encounter filters
758 #
759 # Revision 1.104 2008/05/29 13:26:22 ncq
760 # - add reclassify_documents_by_type
761 #
762 # Revision 1.103 2008/04/11 23:07:22 ncq
763 # - cleanup
764 #
765 # Revision 1.102 2008/02/25 17:31:41 ncq
766 # - logging cleanup
767 #
768 # Revision 1.101 2008/01/30 13:34:50 ncq
769 # - switch to std lib logging
770 #
771 # Revision 1.100 2007/11/05 11:36:29 ncq
772 # - use blobs.delete_document()
773 #
774 # Revision 1.99 2007/10/31 22:06:44 ncq
775 # - delete_document()
776 #
777 # Revision 1.98 2007/10/12 14:15:55 ncq
778 # - cleanup
779 #
780 # Revision 1.97 2007/08/11 23:53:19 ncq
781 # - cleanup
782 # - use gmPG2.bytea2file()
783 #
784 # Revision 1.96 2007/08/09 07:58:44 ncq
785 # - make export_to_file() use gmPG2.bytea2file()
786 # - no more export_to_string()
787 # - comment out __export()
788 # - add display_via_mime()
789 #
790 # Revision 1.95 2007/07/11 21:02:27 ncq
791 # - use gmTools.get_unique_filename()
792 #
793 # Revision 1.94 2007/05/20 01:27:31 ncq
794 # - fix set_translation() - lang needs to go first in i18n.upd_tx()
795 #
796 # Revision 1.93 2007/04/23 01:02:05 ncq
797 # - add set_as_active_photograph()
798 #
799 # Revision 1.92 2007/04/11 14:51:06 ncq
800 # - raising exception on error
801 #
802 # Revision 1.91 2007/03/31 21:18:40 ncq
803 # - apply basename to original filename on save
804 #
805 # Revision 1.90 2007/03/08 16:17:47 ncq
806 # - support blobs.doc_obj.filename
807 #
808 # Revision 1.89 2007/01/10 22:27:53 ncq
809 # - return a list of filenames from export_parts_to_files()
810 #
811 # Revision 1.88 2007/01/07 23:01:26 ncq
812 # - export_to_file(): add filename arg
813 # - export_parts_to_files()
814 #
815 # Revision 1.87 2007/01/06 23:40:49 ncq
816 # - typo in remainder export code in __export
817 #
818 # Revision 1.86 2006/12/11 18:52:11 ncq
819 # - do not delete doc types which are in use
820 #
821 # Revision 1.85 2006/11/20 15:55:41 ncq
822 # - must use return_data when wanting data back from run_rw_queries()
823 #
824 # Revision 1.84 2006/11/06 09:57:39 ncq
825 # - need to return_data to return data
826 # - cannot drop non-user doc types from blobs.doc_type so drop is_user where condition
827 #
828 # Revision 1.83 2006/10/31 17:18:08 ncq
829 # - fix a few programming errors
830 #
831 # Revision 1.82 2006/10/31 16:16:28 ncq
832 # - query strings as unicode
833 #
834 # Revision 1.81 2006/10/08 15:07:11 ncq
835 # - convert to gmPG2
836 # - drop blobs.xlnk_identity support
837 # - use cBusinessDBObject
838 # - adjust queries to schema
839 #
840 # Revision 1.80 2006/09/30 11:38:08 ncq
841 # - remove support for blobs.xlnk_identity
842 #
843 # Revision 1.79 2006/09/28 14:36:10 ncq
844 # - fix search_for_doc(), it used the row, not the value for document instantiation
845 #
846 # Revision 1.78 2006/09/21 19:23:12 ncq
847 # - cast '' to bytea when adding a dummy document part
848 #
849 # Revision 1.77 2006/09/02 21:22:10 ncq
850 # - return new parts from add_parts_from_files()
851 # - cMedDoc.update_data() needs fixing
852 # - forward port test suite improvement and cMedDoc instantiation fix from rel-0-2-patches branch
853 #
854 # Revision 1.76 2006/09/01 14:39:19 ncq
855 # - add FIXME
856 #
857 # Revision 1.75 2006/07/10 21:15:07 ncq
858 # - add cDocumentType
859 # - get_document_types() now returns instances
860 # - add delete_document_type()
861 # - improved testing
862 #
863 # Revision 1.74 2006/07/07 12:06:08 ncq
864 # - return more data from get_document_types()
865 #
866 # Revision 1.73 2006/07/04 21:37:43 ncq
867 # - cleanup
868 # - add create_document_type()
869 #
870 # Revision 1.72 2006/07/01 13:10:13 ncq
871 # - fix __export() re encoding setting
872 #
873 # Revision 1.71 2006/07/01 11:23:35 ncq
874 # - cleanup
875 #
876 # Revision 1.70 2006/06/26 21:37:14 ncq
877 # - properly use explicit encoding setting in put/get
878 # data for document objects
879 #
880 # Revision 1.69 2006/06/21 15:51:48 ncq
881 # - set_intended_reviewer() -> set_primary_reviewer()
882 #
883 # Revision 1.68 2006/06/18 22:43:21 ncq
884 # - missing %
885 #
886 # Revision 1.67 2006/06/18 13:19:55 ncq
887 # - must update XMIN after update_data(_from_file())
888 # - use run_commit2() instead of run_commit()
889 #
890 # Revision 1.66 2006/06/12 20:48:48 ncq
891 # - add missing cleanup() to folder class
892 #
893 # Revision 1.65 2006/06/07 22:07:14 ncq
894 # - use run_commit2() only to ensure better transaction handling
895 # - fix one suspicious faulty handling of run_commit2() return values
896 # - add a "del new_part" just for good measure
897 #
898 # Revision 1.64 2006/06/07 20:22:01 ncq
899 # - must be pk_intended_reviewer
900 #
901 # Revision 1.63 2006/06/05 21:52:00 ncq
902 # - fix one double %
903 #
904 # Revision 1.62 2006/05/31 09:45:19 ncq
905 # - fix doc part saving/locking - reference to PK was wrong, I wonder how it ever worked
906 # - add get_containing_document() and set_intended_reviewer() to cMedDocPart
907 # - make pk_episode in cMedDoc editable
908 # - some more error checking
909 #
910 # Revision 1.61 2006/05/28 15:25:50 ncq
911 # - add "episode" field to doc_part
912 #
913 # Revision 1.60 2006/05/25 22:11:36 ncq
914 # - use blobs.v_obj4doc_no_data
915 #
916 # Revision 1.59 2006/05/20 18:29:21 ncq
917 # - allow setting reviewer in add_parts_from_files()
918 #
919 # Revision 1.58 2006/05/16 15:47:19 ncq
920 # - various small fixes re setting review status
921 #
922 # Revision 1.57 2006/05/08 16:33:02 ncq
923 # - remove useless order by's
924 # - add cMedDoc.has_unreviewed_parts()
925 #
926 # Revision 1.56 2006/05/01 18:43:50 ncq
927 # - handle encounter/episode in create_document()
928 #
929 # Revision 1.55 2006/02/13 08:11:28 ncq
930 # - doc-wide set_reviewed() must be in cMedDoc, not cDocumentFolder
931 # - add cMedDocPart.get_reviews()
932 #
933 # Revision 1.54 2006/02/05 14:36:01 ncq
934 # - intended reviewer now must be staff, not identity
935 #
936 # Revision 1.53 2006/02/05 14:27:13 ncq
937 # - add doc-wide set_reviewed() wrapper to cMedDoc
938 # - handle review-related fields to cMedDocPart
939 # - add set_reviewed() to cMedDocPart
940 #
941 # Revision 1.52 2006/01/27 22:16:14 ncq
942 # - add reviewed/signed to cMedDocPart
943 #
944 # Revision 1.51 2006/01/22 18:07:34 ncq
945 # - set client encoding to sql_ascii where necessary for blobs handling
946 #
947 # Revision 1.50 2006/01/18 23:07:16 ncq
948 # - cleanup
949 #
950 # Revision 1.49 2006/01/17 22:01:35 ncq
951 # - now really sort by date
952 #
953 # Revision 1.48 2006/01/17 20:20:26 ncq
954 # - implement get_ext_ref()
955 #
956 # Revision 1.47 2006/01/16 19:33:46 ncq
957 # - need to "reset client_encoding" on 8.0, too
958 #
959 # Revision 1.46 2006/01/16 19:23:32 ncq
960 # - use reset_encoding on blobs more generously
961 #
962 # Revision 1.45 2006/01/15 16:12:05 ncq
963 # - explicitely use PgUnQuoteBytea() on the concatenated
964 # string when getting Bytea in pieces
965 #
966 # Revision 1.44 2006/01/15 15:06:42 ncq
967 # - return newest-first from get_doc_list()
968 #
969 # Revision 1.43 2006/01/15 14:58:59 ncq
970 # - return translations from get_document_types()
971 #
972 # Revision 1.42 2006/01/15 14:41:21 ncq
973 # - add missing None in call to run_ro_query()
974 #
975 # Revision 1.41 2006/01/14 23:24:00 shilbert
976 # - return doc_type and coresponding primary key not just doc_type string
977 #
978 # Revision 1.40 2006/01/13 13:48:15 ncq
979 # - brush up adding document parts
980 #
981 # Revision 1.39 2006/01/11 22:43:36 ncq
982 # - yet another missed id -> pk
983 #
984 # Revision 1.38 2006/01/11 13:13:53 ncq
985 # - id -> pk
986 #
987 # Revision 1.37 2006/01/09 22:06:09 ncq
988 # - aPKey -> aPK_obj
989 #
990 # Revision 1.36 2006/01/09 10:42:21 ncq
991 # - yet another missed dem schema qualification
992 #
993 # Revision 1.35 2006/01/01 17:39:39 ncq
994 # - require document type in create_document()
995 #
996 # Revision 1.34 2006/01/01 16:08:08 ncq
997 # - proper scoping of functions
998 #
999 # Revision 1.33 2006/01/01 15:39:50 ncq
1000 # - some missing blobs.
1001 #
1002 # Revision 1.32 2005/12/14 17:00:01 ncq
1003 # - add add_document() and add_description() to cMedDoc
1004 # - fix missing ''
1005 # - add gmMedDoc.get_ext_ref()
1006 #
1007 # Revision 1.31 2005/12/13 21:46:07 ncq
1008 # - enhance cMedDoc.add_part() so it can load data from a file
1009 # - add cMedDoc.add_parts_from_files()
1010 #
1011 # Revision 1.30 2005/11/27 09:23:07 ncq
1012 # - added get_document_types()
1013 #
1014 # Revision 1.29 2005/11/01 08:50:24 ncq
1015 # - blobs are in blobs. schema now
1016 #
1017 # Revision 1.28 2005/02/12 13:56:49 ncq
1018 # - identity.id -> identity.pk
1019 #
1020 # Revision 1.27 2005/01/02 19:55:30 ncq
1021 # - don't need _xmins_refetch_col_pos anymore
1022 #
1023 # Revision 1.26 2004/12/20 16:45:49 ncq
1024 # - gmBusinessDBObject now requires refetching of XMIN after save_payload
1025 #
1026 # Revision 1.25 2004/11/03 22:32:34 ncq
1027 # - support _cmds_lock_rows_for_update in business object base class
1028 #
1029 # Revision 1.24 2004/10/11 19:46:52 ncq
1030 # - properly VOify deriving from cBusinessDBObject
1031 # - cMedObj -> cMedDocPart
1032 # - cDocumentFolder.get_documents()
1033 # - cMedDoc.get_descriptions() where max_lng=None will fetch entire description
1034 # - comments, cleanup
1035 #
1036 # Revision 1.23 2004/09/28 12:20:16 ncq
1037 # - cleanup/robustify
1038 # - add get_latest_mugshot()
1039 #
1040 # Revision 1.22 2004/06/01 07:50:01 ncq
1041 # - error checking, optimizing, cleanup
1042 # - adaptation to ClinItem pending
1043 #
1044 # Revision 1.21 2004/03/20 11:16:16 ncq
1045 # - v_18n_doc_type is no more, use _(doc_type.name)
1046 #
1047 # Revision 1.20 2004/03/07 23:49:54 ncq
1048 # - add cDocumentFolder
1049 #
1050 # Revision 1.19 2004/03/04 19:46:53 ncq
1051 # - switch to package based import: from Gnumed.foo import bar
1052 #
1053 # Revision 1.18 2004/03/03 14:41:49 ncq
1054 # - cleanup
1055 # - FIXME: write gmDocumentRecord_SQL
1056 #
1057 # Revision 1.17 2004/03/03 05:24:01 ihaywood
1058 # patient photograph support
1059 #
1060 # Revision 1.16 2004/02/25 09:46:20 ncq
1061 # - import from pycommon now, not python-common
1062 #
1063 # Revision 1.15 2004/01/26 18:19:55 ncq
1064 # - missing :
1065 #
1066 # Revision 1.14 2004/01/18 21:42:17 ncq
1067 # - extra : removed
1068 #
1069 # Revision 1.13 2003/12/29 16:20:28 uid66147
1070 # - use gmPG.run_ro_query/run_commit instead of caching connections ourselves
1071 # - but do establish our own rw connection even for reads since escaping bytea
1072 # over a client_encoding != C breaks transmitted binaries
1073 # - remove deprecated __get/setitem__ API
1074 # - sane create_document/object helpers
1075 #
1076 # Revision 1.12 2003/11/17 10:56:34 sjtan
1077 #
1078 # synced and commiting.
1079 #
1080 # Revision 1.1 2003/10/23 06:02:38 sjtan
1081 #
1082 # manual edit areas modelled after r.terry's specs.
1083 #
1084 # Revision 1.11 2003/06/27 16:04:13 ncq
1085 # - no ; in SQL
1086 #
1087 # Revision 1.10 2003/06/26 21:26:15 ncq
1088 # - cleanup re (cmd,args) and %s; quoting bug
1089 #
1090 # Revision 1.9 2003/04/20 15:32:15 ncq
1091 # - removed __run_query helper
1092 # - call_viewer_on_file moved to gmMimeLib
1093 #
1094 # Revision 1.8 2003/04/18 22:33:44 ncq
1095 # - load document descriptions from database
1096 #
1097 # Revision 1.7 2003/03/31 01:14:22 ncq
1098 # - fixed KeyError on metadata[]
1099 #
1100 # Revision 1.6 2003/03/30 00:18:32 ncq
1101 # - typo
1102 #
1103 # Revision 1.5 2003/03/25 12:37:20 ncq
1104 # - use gmPG helpers
1105 # - clean up code
1106 # - __update_data/metadata - this worked for moving between databases !
1107 #
1108 # Revision 1.4 2003/02/26 23:22:04 ncq
1109 # - metadata write support
1110 #
1111
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Feb 9 04:01:24 2010 | http://epydoc.sourceforge.net |