| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed simple ASCII EMR export tool.
2
3 TODO:
4 - GUI mode:
5 - post-0.1 !
6 - allow user to select patient
7 - allow user to pick episodes/encounters/etc from list
8 - output modes:
9 - HTML - post-0.1 !
10 """
11 #============================================================
12 # $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/client/exporters/gmPatientExporter.py,v $
13 # $Id: gmPatientExporter.py,v 1.138 2009-09-08 17:14:55 ncq Exp $
14 __version__ = "$Revision: 1.138 $"
15 __author__ = "Carlos Moro"
16 __license__ = 'GPL'
17
18 import os.path, sys, types, time, codecs, datetime as pyDT, logging, shutil
19
20
21 import mx.DateTime.Parser as mxParser
22 import mx.DateTime as mxDT
23
24
25 if __name__ == '__main__':
26 sys.path.insert(0, '../../')
27 from Gnumed.pycommon import gmI18N, gmExceptions, gmNull, gmPG2, gmTools
28 from Gnumed.business import gmClinicalRecord, gmPerson, gmAllergy, gmDemographicRecord, gmClinNarrative, gmPersonSearch
29
30
31 _log = logging.getLogger('gm.export')
32 _log.info(__version__)
33 #============================================================
35
36 #--------------------------------------------------------
38 """
39 Constructs a new instance of exporter
40
41 constraints - Exporter constraints for filtering clinical items
42 fileout - File-like object as target for dumping operations
43 """
44 if constraints is None:
45 # default constraints to None for complete emr dump
46 self.__constraints = {
47 'since': None,
48 'until': None,
49 'encounters': None,
50 'episodes': None,
51 'issues': None
52 }
53 else:
54 self.__constraints = constraints
55 self.__target = fileout
56 self.__patient = patient
57 self.lab_new_encounter = True
58 self.__filtered_items = []
59 #--------------------------------------------------------
61 """Sets exporter constraints.
62
63 constraints - Exporter constraints for filtering clinical items
64 """
65 if constraints is None:
66 # default constraints to None for complete emr dump
67 self.__constraints = {
68 'since': None,
69 'until': None,
70 'encounters': None,
71 'episodes': None,
72 'issues': None
73 }
74 else:
75 self.__constraints = constraints
76 return True
77 #--------------------------------------------------------
83 #--------------------------------------------------------
85 """
86 Sets exporter patient
87
88 patient - Patient whose data are to be dumped
89 """
90 if patient is None:
91 _log.error("can't set None patient for exporter")
92 return
93 self.__patient = patient
94 #--------------------------------------------------------
96 """
97 Sets exporter output file
98
99 @param file_name - The file to dump the EMR to
100 @type file_name - FileType
101 """
102 self.__target = target
103 #--------------------------------------------------------
109 #--------------------------------------------------------
115 #--------------------------------------------------------
117 """
118 Retrieves string containg ASCII vaccination table
119 """
120 emr = self.__patient.get_emr()
121 # patient dob
122 patient_dob = self.__patient['dob']
123 date_length = len(patient_dob.strftime('%x')) + 2
124
125 # dictionary of pairs indication : scheduled vaccination
126 vaccinations4regimes = {}
127 for a_vacc_regime in vacc_regimes:
128 indication = a_vacc_regime['indication']
129 vaccinations4regimes[indication] = emr.get_scheduled_vaccinations(indications=[indication])
130 # vaccination regimes count
131 chart_columns = len(vacc_regimes)
132 # foot headers
133 foot_headers = ['last booster', 'next booster']
134 # string for both: ending of vaccinations; non boosters needed
135 ending_str = '='
136
137 # chart row count, columns width and vaccination dictionary of pairs indication : given shot
138 column_widths = []
139 chart_rows = -1
140 vaccinations = {}
141 temp = -1
142 for foot_header in foot_headers: # first column width
143 if len(foot_header) > temp:
144 temp = len(foot_header)
145 column_widths.append(temp)
146 for a_vacc_regime in vacc_regimes:
147 if a_vacc_regime['shots'] > chart_rows: # max_seq -> row count
148 chart_rows = a_vacc_regime['shots']
149 if (len(a_vacc_regime['l10n_indication'])) > date_length: # l10n indication -> column width
150 column_widths.append(len(a_vacc_regime['l10n_indication']))
151 else:
152 column_widths.append(date_length) # date -> column width at least
153 vaccinations[a_vacc_regime['indication']] = emr.get_vaccinations(indications=[a_vacc_regime['indication']]) # given shots 4 indication
154
155 # patient dob in top of vaccination chart
156 txt = '\nDOB: %s' %(patient_dob.strftime('%x')) + '\n'
157
158 # vacc chart table headers
159 # top ---- header line
160 for column_width in column_widths:
161 txt += column_width * '-' + '-'
162 txt += '\n'
163 # indication names header line
164 txt += column_widths[0] * ' ' + '|'
165 col_index = 1
166 for a_vacc_regime in vacc_regimes:
167 txt += a_vacc_regime['l10n_indication'] + (column_widths[col_index] - len(a_vacc_regime['l10n_indication'])) * ' ' + '|'
168 col_index += 1
169 txt += '\n'
170 # bottom ---- header line
171 for column_width in column_widths:
172 txt += column_width * '-' + '-'
173 txt += '\n'
174
175 # vacc chart data
176 due_date = None
177 # previously displayed date list
178 prev_displayed_date = [patient_dob]
179 for a_regime in vacc_regimes:
180 prev_displayed_date.append(patient_dob) # initialice with patient dob (useful for due first shot date calculation)
181 # iterate data rows
182 for row_index in range(0, chart_rows):
183 row_header = '#%s' %(row_index+1)
184 txt += row_header + (column_widths[0] - len(row_header)) * ' ' + '|'
185
186 for col_index in range(1, chart_columns+1):
187 indication =vacc_regimes[col_index-1]['indication']
188 seq_no = vacc_regimes[col_index-1]['shots']
189 if row_index == seq_no: # had just ended scheduled vaccinations
190 txt += ending_str * column_widths[col_index] + '|'
191 elif row_index < seq_no: # vaccination scheduled
192 try:
193 vacc_date = vaccinations[indication][row_index]['date'] # vaccination given
194 vacc_date_str = vacc_date.strftime('%x')
195 txt += vacc_date_str + (column_widths[col_index] - len(vacc_date_str)) * ' ' + '|'
196 prev_displayed_date[col_index] = vacc_date
197 except:
198 if row_index == 0: # due first shot
199 due_date = prev_displayed_date[col_index] + vaccinations4regimes[indication][row_index]['age_due_min'] # FIXME 'age_due_min' not properly retrieved
200 else: # due any other than first shot
201 due_date = prev_displayed_date[col_index] + vaccinations4regimes[indication][row_index]['min_interval']
202 txt += '('+ due_date.strftime('%Y-%m-%d') + ')' + (column_widths[col_index] - date_length) * ' ' + '|'
203 prev_displayed_date[col_index] = due_date
204 else: # not scheduled vaccination at that position
205 txt += column_widths[col_index] * ' ' + '|'
206 txt += '\n' # end of scheduled vaccination dates display
207 for column_width in column_widths: # ------ separator line
208 txt += column_width * '-' + '-'
209 txt += '\n'
210
211 # scheduled vaccination boosters (date retrieving)
212 all_vreg_boosters = []
213 for a_vacc_regime in vacc_regimes:
214 vaccs4indication = vaccinations[a_vacc_regime['indication']] # iterate over vaccinations by indication
215 given_boosters = [] # will contain given boosters for current indication
216 for a_vacc in vaccs4indication:
217 try:
218 if a_vacc['is_booster']:
219 given_boosters.append(a_vacc)
220 except:
221 # not a booster
222 pass
223 if len(given_boosters) > 0:
224 all_vreg_boosters.append(given_boosters[len(given_boosters)-1]) # last of given boosters
225 else:
226 all_vreg_boosters.append(None)
227
228 # next booster in schedule
229 all_next_boosters = []
230 for a_booster in all_vreg_boosters:
231 all_next_boosters.append(None)
232 # scheduled vaccination boosters (displaying string)
233 cont = 0
234 for a_vacc_regime in vacc_regimes:
235 vaccs = vaccinations4regimes[a_vacc_regime['indication']]
236 if vaccs[len(vaccs)-1]['is_booster'] == False: # booster is not scheduled/needed
237 all_vreg_boosters[cont] = ending_str * column_widths[cont+1]
238 all_next_boosters[cont] = ending_str * column_widths[cont+1]
239 else:
240 indication = vacc_regimes[cont]['indication']
241 if len(vaccinations[indication]) > vacc_regimes[cont]['shots']: # boosters given
242 all_vreg_boosters[cont] = vaccinations[indication][len(vaccinations[indication])-1]['date'].strftime('%Y-%m-%d') # show last given booster date
243 scheduled_booster = vaccinations4regimes[indication][len(vaccinations4regimes[indication])-1]
244 booster_date = vaccinations[indication][len(vaccinations[indication])-1]['date'] + scheduled_booster['min_interval']
245 if booster_date < mxDT.today():
246 all_next_boosters[cont] = '<(' + booster_date.strftime('%Y-%m-%d') + ')>' # next booster is due
247 else:
248 all_next_boosters[cont] = booster_date.strftime('%Y-%m-%d')
249 elif len(vaccinations[indication]) == vacc_regimes[cont]['shots']: # just finished vaccinations, begin boosters
250 all_vreg_boosters[cont] = column_widths[cont+1] * ' '
251 scheduled_booster = vaccinations4regimes[indication][len(vaccinations4regimes[indication])-1]
252 booster_date = vaccinations[indication][len(vaccinations[indication])-1]['date'] + scheduled_booster['min_interval']
253 if booster_date < mxDT.today():
254 all_next_boosters[cont] = '<(' + booster_date.strftime('%Y-%m-%d') + ')>' # next booster is due
255 else:
256 all_next_boosters[cont] = booster_date.strftime('%Y-%m-%d')
257 else:
258 all_vreg_boosters[cont] = column_widths[cont+1] * ' ' # unfinished schedule
259 all_next_boosters[cont] = column_widths[cont+1] * ' '
260 cont += 1
261
262 # given boosters
263 foot_header = foot_headers[0]
264 col_index = 0
265 txt += foot_header + (column_widths[0] - len(foot_header)) * ' ' + '|'
266 col_index += 1
267 for a_vacc_regime in vacc_regimes:
268 txt += str(all_vreg_boosters[col_index-1]) + (column_widths[col_index] - len(str(all_vreg_boosters[col_index-1]))) * ' ' + '|'
269 col_index += 1
270 txt += '\n'
271 for column_width in column_widths:
272 txt += column_width * '-' + '-'
273 txt += '\n'
274
275 # next booster
276 foot_header = foot_headers[1]
277 col_index = 0
278 txt += foot_header + (column_widths[0] - len(foot_header)) * ' ' + '|'
279 col_index += 1
280 for a_vacc_regime in vacc_regimes:
281 txt += str(all_next_boosters[col_index-1]) + (column_widths[col_index] - len(str(all_next_boosters[col_index-1]))) * ' ' + '|'
282 col_index += 1
283 txt += '\n'
284 for column_width in column_widths:
285 txt += column_width * '-' + '-'
286 txt += '\n'
287
288 self.__target.write(txt)
289 #--------------------------------------------------------
291 """
292 Iterate over patient scheduled regimes preparing vacc tables dump
293 """
294
295 emr = self.__patient.get_emr()
296
297 # vaccination regimes
298 all_vacc_regimes = emr.get_scheduled_vaccination_regimes()
299 # Configurable: vacc regimes per displayed table
300 # FIXME: option, post 0.1 ?
301 max_regs_per_table = 4
302
303 # Iterate over patient scheduled regimes dumping in tables of
304 # max_regs_per_table regimes per table
305 reg_count = 0
306 vacc_regimes = []
307 for total_reg_count in range(0,len(all_vacc_regimes)):
308 if reg_count%max_regs_per_table == 0:
309 if len(vacc_regimes) > 0:
310 self.__dump_vacc_table(vacc_regimes)
311 vacc_regimes = []
312 reg_count = 0
313 vacc_regimes.append(all_vacc_regimes[total_reg_count])
314 reg_count += 1
315 if len(vacc_regimes) > 0:
316 self.__dump_vacc_table(vacc_regimes)
317
318 #--------------------------------------------------------
320 """
321 Dump information related to the fields of a clinical item
322 offset - Number of left blank spaces
323 item - Item of the field to dump
324 fields - Fields to dump
325 """
326 txt = ''
327 for a_field in field_list:
328 if type(a_field) is not types.UnicodeType:
329 a_field = unicode(a_field, encoding='latin1', errors='replace')
330 txt += u'%s%s%s' % ((offset * u' '), a_field, gmTools.coalesce(item[a_field], u'\n', template_initial = u': %s\n'))
331 return txt
332 #--------------------------------------------------------
334 """
335 Dumps allergy item data
336 allergy - Allergy item to dump
337 left_margin - Number of spaces on the left margin
338 """
339 txt = ''
340 txt += left_margin*' ' + _('Allergy') + ': \n'
341 txt += self.dump_item_fields((left_margin+3), allergy, ['allergene', 'substance', 'generic_specific','l10n_type', 'definite', 'reaction'])
342 return txt
343 #--------------------------------------------------------
345 """
346 Dumps vaccination item data
347 vaccination - Vaccination item to dump
348 left_margin - Number of spaces on the left margin
349 """
350 txt = ''
351 txt += left_margin*' ' + _('Vaccination') + ': \n'
352 txt += self.dump_item_fields((left_margin+3), vaccination, ['l10n_indication', 'vaccine', 'batch_no', 'site', 'narrative'])
353 return txt
354 #--------------------------------------------------------
356 """
357 Dumps lab result item data
358 lab_request - Lab request item to dump
359 left_margin - Number of spaces on the left margin
360 """
361 txt = ''
362 if self.lab_new_encounter:
363 txt += (left_margin)*' ' + _('Lab result') + ': \n'
364 txt += (left_margin+3) * ' ' + lab_result['unified_name'] + ': ' + lab_result['unified_val']+ ' ' + lab_result['val_unit'] + ' (' + lab_result['material'] + ')' + '\n'
365 return txt
366 #--------------------------------------------------------
368 """
369 Obtains formatted clinical item output dump
370 item - The clinical item to dump
371 left_margin - Number of spaces on the left margin
372 """
373 txt = ''
374 if isinstance(item, gmAllergy.cAllergy):
375 txt += self.get_allergy_output(item, left_margin)
376 # elif isinstance(item, gmVaccination.cVaccination):
377 # txt += self.get_vaccination_output(item, left_margin)
378 # elif isinstance(item, gmPathLab.cLabResult):
379 # txt += self.get_lab_result_output(item, left_margin)
380 # self.lab_new_encounter = False
381 return txt
382 #--------------------------------------------------------
384 """
385 Retrieve patient clinical items filtered by multiple constraints
386 """
387 if not self.__patient.connected:
388 return False
389 emr = self.__patient.get_emr()
390 filtered_items = []
391 filtered_items.extend(emr.get_allergies(
392 since=self.__constraints['since'],
393 until=self.__constraints['until'],
394 encounters=self.__constraints['encounters'],
395 episodes=self.__constraints['episodes'],
396 issues=self.__constraints['issues']))
397 # try:
398 # filtered_items.extend(emr.get_vaccinations(
399 # since=self.__constraints['since'],
400 # until=self.__constraints['until'],
401 # encounters=self.__constraints['encounters'],
402 # episodes=self.__constraints['episodes'],
403 # issues=self.__constraints['issues']))
404 # except:
405 # _log.error("vaccination error? outside regime")
406
407 # filtered_items.extend(emr.get_lab_results(
408 # since=self.__constraints['since'],
409 # until=self.__constraints['until'],
410 # encounters=self.__constraints['encounters'],
411 # episodes=self.__constraints['episodes'],
412 # issues=self.__constraints['issues']))
413 self.__filtered_items = filtered_items
414 return True
415 #--------------------------------------------------------
417 """
418 Dumps allergy item data summary
419 allergy - Allergy item to dump
420 left_margin - Number of spaces on the left margin
421 """
422 txt = _('%sAllergy: %s, %s (noted %s)\n') % (
423 left_margin * u' ',
424 allergy['descriptor'],
425 gmTools.coalesce(allergy['reaction'], _('unknown reaction')),
426 allergy['date'].strftime('%x')
427 )
428 # txt = left_margin * ' ' \
429 # + _('Allergy') + ': ' \
430 # + allergy['descriptor'] + u', ' \
431 # + gmTools.coalesce(allergy['reaction'], _('unknown reaction')) ' ' \
432 # + _('(noted %s)') % allergy['date'].strftime('%x') \
433 # + '\n'
434 return txt
435 #--------------------------------------------------------
437 """
438 Dumps vaccination item data summary
439 vaccination - Vaccination item to dump
440 left_margin - Number of spaces on the left margin
441 """
442 txt = left_margin*' ' + _('Vaccination') + ': ' + vaccination['l10n_indication'] + ', ' + \
443 vaccination['narrative'] + '\n'
444 return txt
445 #--------------------------------------------------------
447 """
448 Dumps lab result item data summary
449 lab_request - Lab request item to dump
450 left_margin - Number of spaces on the left margin
451 """
452 txt = ''
453 if self.lab_new_encounter:
454 txt += (left_margin+3)*' ' + _('Lab') + ': ' + \
455 lab_result['unified_name'] + '-> ' + lab_result['unified_val'] + \
456 ' ' + lab_result['val_unit']+ '\n' + '(' + lab_result['req_when'].strftime('%Y-%m-%d') + ')'
457 return txt
458 #--------------------------------------------------------
460 """
461 Obtains formatted clinical item summary dump
462 item - The clinical item to dump
463 left_margin - Number of spaces on the left margin
464 """
465 txt = ''
466 if isinstance(item, gmAllergy.cAllergy):
467 txt += self.get_allergy_summary(item, left_margin)
468 # elif isinstance(item, gmVaccination.cVaccination):
469 # txt += self.get_vaccination_summary(item, left_margin)
470 # elif isinstance(item, gmPathLab.cLabResult) and \
471 # True:
472 # #(item['relevant'] == True or item['abnormal'] == True):
473 # txt += self.get_lab_result_summary(item, left_margin)
474 # self.lab_new_encounter = False
475 return txt
476 #--------------------------------------------------------
478 """
479 checks a emr_tree constructed with this.get_historical_tree()
480 and sees if any new items need to be inserted.
481 """
482 #TODO , caching eliminates tree update time, so don't really need this
483 self._traverse_health_issues( emr_tree, self._update_health_issue_branch)
484 #--------------------------------------------------------
487 #--------------------------------------------------------
489 """
490 Retrieves patient's historical in form of a wx tree of health issues
491 -> episodes
492 -> encounters
493 Encounter object is associated with item to allow displaying its information
494 """
495 # variable initialization
496 # this protects the emrBrowser from locking up in a paint event, e.g. in
497 # some configurations which want to use emrBrowser, but don't stop tabbing
498 # to emr browser when no patient selected. the effect is to displace a cNull instance
499 # which is a sane representation when no patient is selected.
500 if not self.__fetch_filtered_items():
501 return
502 emr = self.__patient.get_emr()
503 unlinked_episodes = emr.get_episodes(issues = [None])
504 h_issues = []
505 h_issues.extend(emr.get_health_issues(id_list = self.__constraints['issues']))
506 # build the tree
507 # unlinked episodes
508 if len(unlinked_episodes) > 0:
509 h_issues.insert(0, {
510 'description': _('Unattributed episodes'),
511 'pk_health_issue': None
512 })
513 # existing issues
514 for a_health_issue in h_issues:
515 health_issue_action( emr_tree, a_health_issue)
516
517 emr_tree.SortChildren(emr_tree.GetRootItem())
518 #--------------------------------------------------------
520 """appends to a wx emr_tree , building wx treenodes from the health_issue make this reusable for non-collapsing tree updates"""
521 emr = self.__patient.get_emr()
522 root_node = emr_tree.GetRootItem()
523 issue_node = emr_tree.AppendItem(root_node, a_health_issue['description'])
524 emr_tree.SetPyData(issue_node, a_health_issue)
525 episodes = emr.get_episodes(id_list=self.__constraints['episodes'], issues = [a_health_issue['pk_health_issue']])
526 for an_episode in episodes:
527 self._add_episode_to_tree( emr, emr_tree, issue_node,a_health_issue, an_episode)
528 emr_tree.SortChildren(issue_node)
529 #--------------------------------------------------------
531 episode_node = emr_tree.AppendItem(issue_node, an_episode['description'])
532 emr_tree.SetPyData(episode_node, an_episode)
533 if an_episode['episode_open']:
534 emr_tree.SetItemBold(issue_node, True)
535
536 encounters = self._get_encounters( an_episode, emr )
537 self._add_encounters_to_tree( encounters, emr_tree, episode_node )
538 emr_tree.SortChildren(episode_node)
539 return episode_node
540 #--------------------------------------------------------
542 for an_encounter in encounters:
543 # label = u'%s: %s' % (an_encounter['started'].strftime('%Y-%m-%d'), an_encounter['l10n_type'])
544 label = u'%s: %s' % (
545 an_encounter['started'].strftime('%Y-%m-%d'),
546 gmTools.unwrap (
547 gmTools.coalesce (
548 gmTools.coalesce (
549 gmTools.coalesce (
550 an_encounter.get_latest_soap ( # soAp
551 soap_cat = 'a',
552 episode = emr_tree.GetPyData(episode_node)['pk_episode']
553 ),
554 an_encounter['assessment_of_encounter'] # or AOE
555 ),
556 an_encounter['reason_for_encounter'] # or RFE
557 ),
558 an_encounter['l10n_type'] # or type
559 ),
560 max_length = 40
561 )
562 )
563 encounter_node_id = emr_tree.AppendItem(episode_node, label)
564 emr_tree.SetPyData(encounter_node_id, an_encounter)
565 #--------------------------------------------------------
567 encounters = emr.get_encounters (
568 episodes = [an_episode['pk_episode']]
569 )
570 return encounters
571 #--------------------------------------------------------
573 emr = self.__patient.get_emr()
574 root_node = emr_tree.GetRootItem()
575 id, cookie = emr_tree.GetFirstChild(root_node)
576 found = False
577 while id.IsOk():
578 if emr_tree.GetItemText(id) == a_health_issue['description']:
579 found = True
580 break
581 id,cookie = emr_tree.GetNextChild( root_node, cookie)
582
583 if not found:
584 _log.error("health issue %s should exist in tree already", a_health_issue['description'] )
585 return
586 issue_node = id
587 episodes = emr.get_episodes(id_list=self.__constraints['episodes'], issues = [a_health_issue['pk_health_issue']])
588
589 #check for removed episode and update tree
590 tree_episodes = {}
591 id_episode, cookie = emr_tree.GetFirstChild(issue_node)
592 while id_episode.IsOk():
593 tree_episodes[ emr_tree.GetPyData(id_episode)['pk_episode'] ]= id_episode
594 id_episode,cookie = emr_tree.GetNextChild( issue_node, cookie)
595
596 existing_episode_pk = [ e['pk_episode'] for e in episodes]
597 missing_tree_pk = [ pk for pk in tree_episodes.keys() if pk not in existing_episode_pk]
598 for pk in missing_tree_pk:
599 emr_tree.Remove( tree_episodes[pk] )
600
601 added_episode_pk = [pk for pk in existing_episode_pk if pk not in tree_episodes.keys()]
602 add_episodes = [ e for e in episodes if e['pk_episode'] in added_episode_pk]
603
604 #check for added episodes and update tree
605 for an_episode in add_episodes:
606 node = self._add_episode_to_tree( emr, emr_tree, issue_node, a_health_issue, an_episode)
607 tree_episodes[an_episode['pk_episode']] = node
608
609 for an_episode in episodes:
610 # found episode, check for encounter change
611 try:
612 #print "getting id_episode of ", an_episode['pk_episode']
613 id_episode = tree_episodes[an_episode['pk_episode']]
614 except:
615 import pdb
616 pdb.set_trace()
617 # get a map of encounters in the tree by pk_encounter as key
618 tree_enc = {}
619 id_encounter, cookie = emr_tree.GetFirstChild(id_episode)
620 while id_encounter.IsOk():
621 tree_enc[ emr_tree.GetPyData(id_encounter)['pk_encounter'] ] = id_encounter
622 id_encounter,cookie = emr_tree.GetNextChild(id_episode, cookie)
623
624 # remove encounters in tree not in existing encounters in episode
625 # encounters = self._get_encounters( a_health_issue, an_episode, emr )
626 encounters = self._get_encounters( an_episode, emr )
627 existing_enc_pk = [ enc['pk_encounter'] for enc in encounters]
628 missing_enc_pk = [ pk for pk in tree_enc.keys() if pk not in existing_enc_pk]
629 for pk in missing_enc_pk:
630 emr_tree.Remove( tree_enc[pk] )
631
632 # check for added encounter
633 added_enc_pk = [ pk for pk in existing_enc_pk if pk not in tree_enc.keys() ]
634 add_encounters = [ enc for enc in encounters if enc['pk_encounter'] in added_enc_pk]
635 if add_encounters != []:
636 #print "DEBUG found encounters to add"
637 self._add_encounters_to_tree( add_encounters, emr_tree, id_episode)
638 #--------------------------------------------------------
640 """
641 Dumps patient EMR summary
642 """
643 txt = ''
644 for an_item in self.__filtered_items:
645 txt += self.get_item_summary(an_item, left_margin)
646 return txt
647 #--------------------------------------------------------
649 """Dumps episode specific data"""
650 emr = self.__patient.get_emr()
651 encs = emr.get_encounters(episodes = [episode['pk_episode']])
652 if encs is None:
653 txt = left_margin * ' ' + _('Error retrieving encounters for episode\n%s') % str(episode)
654 return txt
655 no_encs = len(encs)
656 if no_encs == 0:
657 txt = left_margin * ' ' + _('There are no encounters for this episode.')
658 return txt
659 if episode['episode_open']:
660 status = _('active')
661 else:
662 status = _('finished')
663 first_encounter = emr.get_first_encounter(episode_id = episode['pk_episode'])
664 last_encounter = emr.get_last_encounter(episode_id = episode['pk_episode'])
665 txt = _(
666 '%sEpisode "%s" [%s]\n'
667 '%sEncounters: %s (%s - %s)\n'
668 '%sLast worked on: %s\n'
669 ) % (
670 left_margin * ' ', episode['description'], status,
671 left_margin * ' ', no_encs, first_encounter['started'].strftime('%m/%Y'), last_encounter['last_affirmed'].strftime('%m/%Y'),
672 left_margin * ' ', last_encounter['last_affirmed'].strftime('%Y-%m-%d %H:%M')
673 )
674 return txt
675 #--------------------------------------------------------
677 """
678 Dumps encounter specific data (rfe, aoe and soap)
679 """
680 emr = self.__patient.get_emr()
681 # general
682 txt = (' ' * left_margin) + '#%s: %s - %s %s' % (
683 encounter['pk_encounter'],
684 encounter['started'].strftime('%Y-%m-%d %H:%M'),
685 encounter['last_affirmed'].strftime('%H:%M (%Z)'),
686 encounter['l10n_type']
687 )
688 if (encounter['assessment_of_encounter'] is not None) and (len(encounter['assessment_of_encounter']) > 0):
689 txt += ' "%s"' % encounter['assessment_of_encounter']
690 txt += '\n\n'
691
692 # rfe/aoe
693 txt += (' ' * left_margin) + '%s: %s\n' % (_('RFE'), encounter['reason_for_encounter'])
694 txt += (' ' * left_margin) + '%s: %s\n' % (_('AOE'), encounter['assessment_of_encounter'])
695
696 # soap
697 soap_cat_labels = {
698 's': _('Subjective'),
699 'o': _('Objective'),
700 'a': _('Assessment'),
701 'p': _('Plan'),
702 None: _('Administrative')
703 }
704 eol_w_margin = '\n' + (' ' * (left_margin+3))
705 for soap_cat in 'soap':
706 soap_cat_narratives = emr.get_clin_narrative (
707 episodes = [episode['pk_episode']],
708 encounters = [encounter['pk_encounter']],
709 soap_cats = [soap_cat]
710 )
711 if soap_cat_narratives is None:
712 continue
713 if len(soap_cat_narratives) == 0:
714 continue
715 txt += (' ' * left_margin) + soap_cat_labels[soap_cat] + ':\n'
716 for soap_entry in soap_cat_narratives:
717 txt += gmTools.wrap (
718 '%s %.8s: %s\n' % (
719 soap_entry['date'].strftime('%d.%m. %H:%M'),
720 soap_entry['provider'],
721 soap_entry['narrative']
722 ), 75
723 )
724
725 # txt += (
726 # (' ' * (left_margin+3)) +
727 # soap_entry['date'].strftime('%H:%M %.8s: ') % soap_entry['provider'] +
728 # soap_entry['narrative'].replace('\n', eol_w_margin) +
729 # '\n'
730 # )
731 #FIXME: add diagnoses
732
733 # items
734 for an_item in self.__filtered_items:
735 if an_item['pk_encounter'] == encounter['pk_encounter']:
736 txt += self.get_item_output(an_item, left_margin)
737 return txt
738 #--------------------------------------------------------
740 """Dumps patient's historical in form of a tree of health issues
741 -> episodes
742 -> encounters
743 -> clinical items
744 """
745
746 # fecth all values
747 self.__fetch_filtered_items()
748 emr = self.__patient.get_emr()
749
750 # dump clinically relevant items summary
751 for an_item in self.__filtered_items:
752 self.__target.write(self.get_item_summary(an_item, 3))
753
754 # begin with the tree
755 h_issues = []
756 h_issues.extend(emr.get_health_issues(id_list = self.__constraints['issues']))
757 # unlinked episodes
758 unlinked_episodes = emr.get_episodes(issues = [None])
759 if len(unlinked_episodes) > 0:
760 h_issues.insert(0, {'description':_('Unattributed episodes'), 'pk_health_issue':None})
761 for a_health_issue in h_issues:
762 self.__target.write('\n' + 3*' ' + 'Health Issue: ' + a_health_issue['description'] + '\n')
763 episodes = emr.get_episodes(id_list=self.__constraints['episodes'], issues = [a_health_issue['pk_health_issue']])
764 for an_episode in episodes:
765 self.__target.write('\n' + 6*' ' + 'Episode: ' + an_episode['description'] + '\n')
766 if a_health_issue['pk_health_issue'] is None:
767 issues = None
768 else:
769 issues = [a_health_issue['pk_health_issue']]
770 encounters = emr.get_encounters (
771 since = self.__constraints['since'],
772 until = self.__constraints['until'],
773 id_list = self.__constraints['encounters'],
774 episodes = [an_episode['pk_episode']],
775 issues = issues
776 )
777 for an_encounter in encounters:
778 # title
779 self.lab_new_encounter = True
780 self.__target.write(
781 '\n %s %s: %s - %s (%s)\n' % (
782 _('Encounter'),
783 an_encounter['l10n_type'],
784 an_encounter['started'].strftime('%A, %Y-%m-%d %H:%M'),
785 an_encounter['last_affirmed'].strftime('%m-%d %H:%M'),
786 an_encounter['assessment_of_encounter']
787 )
788 )
789 self.__target.write(self.get_encounter_info(an_episode, an_encounter, 12))
790 #--------------------------------------------------------
792 """
793 Dumps in ASCII format patient's clinical record
794 """
795 emr = self.__patient.get_emr()
796 if emr is None:
797 _log.error('cannot get EMR text dump')
798 print(_(
799 'An error occurred while retrieving a text\n'
800 'dump of the EMR for the active patient.\n\n'
801 'Please check the log file for details.'
802 ))
803 return None
804 self.__target.write('\nOverview\n')
805 self.__target.write('--------\n')
806 self.__target.write("1) Allergy status (for details, see below):\n\n")
807 for allergy in emr.get_allergies():
808 self.__target.write(" " + allergy['descriptor'] + "\n\n")
809 self.__target.write("2) Vaccination status (* indicates booster):\n")
810 # self.get_vacc_table()
811 self.__target.write("\n3) Historical:\n\n")
812 self.dump_historical_tree()
813
814 try:
815 emr.cleanup()
816 except:
817 print "error cleaning up EMR"
818 #--------------------------------------------------------
820 """
821 Dumps patient stored medical documents
822
823 """
824 doc_folder = self.__patient.get_document_folder()
825
826 self.__target.write('\n4) Medical documents: (date) reference - type "comment"\n')
827 self.__target.write(' object - comment')
828
829 docs = doc_folder.get_documents()
830 for doc in docs:
831 self.__target.write('\n\n (%s) %s - %s "%s"' % (
832 doc['clin_when'].strftime('%Y-%m-%d'),
833 doc['ext_ref'],
834 doc['l10n_type'],
835 doc['comment'])
836 )
837 for part in doc.parts:
838 self.__target.write('\n %s - %s' % (
839 part['seq_idx'],
840 part['obj_comment'])
841 )
842 self.__target.write('\n\n')
843 #--------------------------------------------------------
845 """
846 Dumps in ASCII format some basic patient's demographic data
847 """
848 if self.__patient is None:
849 _log.error('cannot get Demographic export')
850 print(_(
851 'An error occurred while Demographic record export\n'
852 'Please check the log file for details.'
853 ))
854 return None
855
856 self.__target.write('\n\n\nDemographics')
857 self.__target.write('\n------------\n')
858 self.__target.write(' Id: %s \n' % self.__patient['pk_identity'])
859 cont = 0
860 for name in self.__patient.get_names():
861 if cont == 0:
862 self.__target.write(' Name (Active): %s, %s\n' % (name['firstnames'], name['lastnames']) )
863 else:
864 self.__target.write(' Name %s: %s, %s\n' % (cont, name['firstnames'], name['lastnames']))
865 cont += 1
866 self.__target.write(' Gender: %s\n' % self.__patient['gender'])
867 self.__target.write(' Title: %s\n' % self.__patient['title'])
868 self.__target.write(' Dob: %s\n' % self.__patient.get_formatted_dob(format = '%Y-%m-%d'))
869 self.__target.write(' Medical age: %s\n' % self.__patient.get_medical_age())
870 #--------------------------------------------------------
872 """
873 Dumps exporter filtering constraints
874 """
875 self.__first_constraint = True
876 if not self.__constraints['since'] is None:
877 self.dump_constraints_header()
878 self.__target.write('\nSince: %s' % self.__constraints['since'].strftime('%Y-%m-%d'))
879
880 if not self.__constraints['until'] is None:
881 self.dump_constraints_header()
882 self.__target.write('\nUntil: %s' % self.__constraints['until'].strftime('%Y-%m-%d'))
883
884 if not self.__constraints['encounters'] is None:
885 self.dump_constraints_header()
886 self.__target.write('\nEncounters: ')
887 for enc in self.__constraints['encounters']:
888 self.__target.write(str(enc) + ' ')
889
890 if not self.__constraints['episodes'] is None:
891 self.dump_constraints_header()
892 self.__target.write('\nEpisodes: ')
893 for epi in self.__constraints['episodes']:
894 self.__target.write(str(epi) + ' ')
895
896 if not self.__constraints['issues'] is None:
897 self.dump_constraints_header()
898 self.__target.write('\nIssues: ')
899 for iss in self.__constraints['issues']:
900 self.__target.write(str(iss) + ' ')
901 #--------------------------------------------------------
910 #============================================================
912 """Exports patient EMR into a simple chronological journal.
913
914 Note that this export will emit u'' strings only.
915 """
918 #--------------------------------------------------------
919 # external API
920 #--------------------------------------------------------
922 """Export medical record into a file.
923
924 @type filename: None (creates filename by itself) or string
925 @type patient: None (use currently active patient) or <gmPerson.cIdentity> instance
926 """
927 if patient is None:
928 patient = gmPerson.gmCurrentPatient()
929 if not patient.connected:
930 raise ValueError('[%s].export_to_file(): no active patient' % self.__class__.__name__)
931
932 if filename is None:
933 filename = u'%s-%s-%s-%s.txt' % (
934 _('emr-journal'),
935 patient['lastnames'].replace(u' ', u'_'),
936 patient['firstnames'].replace(u' ', u'_'),
937 patient.get_formatted_dob(format = '%Y-%m-%d')
938 )
939 path = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', patient['dirname'], filename))
940
941 f = codecs.open(filename = filename, mode = 'w+b', encoding = 'utf8', errors = 'replace')
942 self.export(target = f, patient = patient)
943 f.close()
944 return filename
945 #--------------------------------------------------------
946 # internal API
947 #--------------------------------------------------------
949 """
950 Export medical record into a Python object.
951
952 @type target: a python object supporting the write() API
953 @type patient: None (use currently active patient) or <gmPerson.cIdentity> instance
954 """
955 if patient is None:
956 patient = gmPerson.gmCurrentPatient()
957 if not patient.connected:
958 raise ValueError('[%s].export(): no active patient' % self.__class__.__name__)
959
960 # write header
961 txt = _('Chronological EMR Journal\n')
962 target.write(txt)
963 target.write(u'=' * (len(txt)-1))
964 target.write('\n')
965 target.write(_('Patient: %s (%s), No: %s\n') % (patient['description'], patient['gender'], patient['pk_identity']))
966 target.write(_('Born : %s, age: %s\n\n') % (
967 patient.get_formatted_dob(format = '%x', encoding = gmI18N.get_encoding()),
968 patient.get_medical_age()
969 ))
970 target.write(u'.-%10.10s---%9.9s-------%72.72s\n' % (u'-' * 10, u'-' * 9, u'-' * self.__part_len))
971 target.write(u'| %10.10s | %9.9s | | %s\n' % (_('Happened'), _('Doc'), _('Narrative')))
972 target.write(u'|-%10.10s---%9.9s-------%72.72s\n' % (u'-' * 10, u'-' * 9, u'-' * self.__part_len))
973
974 # get data
975 cmd = u"""
976 select
977 to_char(vemrj.clin_when, 'YYYY-MM-DD') as date,
978 vemrj.*,
979 (select rank from clin.soap_cat_ranks where soap_cat = vemrj.soap_cat) as scr,
980 to_char(vemrj.modified_when, 'YYYY-MM-DD HH24:MI') as date_modified
981 from clin.v_emr_journal vemrj
982 where pk_patient = %s
983 order by date, pk_episode, scr, src_table"""
984 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [patient['pk_identity']]}], get_col_idx = True)
985
986 # write data
987 prev_date = u''
988 prev_doc = u''
989 prev_soap = u''
990 for row in rows:
991 # narrative
992 if row['narrative'] is None:
993 continue
994
995 txt = gmTools.wrap (
996 text = row['narrative'].replace(u'\r', u'') + (u' (%s)' % row['date_modified']),
997 width = self.__part_len
998 ).split('\n')
999
1000 # same provider ?
1001 curr_doc = row['modified_by']
1002 if curr_doc != prev_doc:
1003 prev_doc = curr_doc
1004 else:
1005 curr_doc = u''
1006
1007 # same soap category ?
1008 curr_soap = row['soap_cat']
1009 if curr_soap != prev_soap:
1010 prev_soap = curr_soap
1011
1012 # same date ?
1013 curr_date = row['date']
1014 if curr_date != prev_date:
1015 prev_date = curr_date
1016 curr_doc = row['modified_by']
1017 prev_doc = curr_doc
1018 curr_soap = row['soap_cat']
1019 prev_soap = curr_soap
1020 else:
1021 curr_date = u''
1022
1023 # display first part
1024 target.write(u'| %10.10s | %9.9s | %3.3s | %s\n' % (
1025 curr_date,
1026 curr_doc,
1027 gmClinNarrative.soap_cat2l10n[curr_soap],
1028 txt[0]
1029 ))
1030
1031 # only one part ?
1032 if len(txt) == 1:
1033 continue
1034
1035 template = u'| %10.10s | %9.9s | %3.3s | %s\n'
1036 for part in txt[1:]:
1037 line = template % (u'', u'', u' ', part)
1038 target.write(line)
1039
1040 # write footer
1041 target.write(u'`-%10.10s---%9.9s-------%72.72s\n\n' % (u'-' * 10, u'-' * 9, u'-' * self.__part_len))
1042 target.write(_('Exported: %s\n') % pyDT.datetime.now().strftime('%c').decode(gmI18N.get_encoding()))
1043
1044 return
1045 #============================================================
1047 """Export SOAP data per encounter into Medistar import format."""
1049 if patient is None:
1050 self.__pat = gmPerson.gmCurrentPatient()
1051 else:
1052 if not isinstance(patient, gmPerson.cIdentity):
1053 raise gmExceptions.ConstructorError, '<patient> argument must be instance of <cIdentity>, but is: %s' % type(patient)
1054 self.__pat = patient
1055 #--------------------------------------------------------
1056 # external API
1057 #--------------------------------------------------------
1058 - def export_to_file(self, filename=None, encounter=None, soap_cats=u'soap', export_to_import_file=False):
1059 if not self.__pat.connected:
1060 return (False, 'no active patient')
1061
1062 if filename is None:
1063 path = os.path.abspath(os.path.expanduser('~/gnumed/export'))
1064 filename = '%s-%s-%s-%s-%s.txt' % (
1065 os.path.join(path, 'Medistar-MD'),
1066 time.strftime('%Y-%m-%d',time.localtime()),
1067 self.__pat['lastnames'].replace(' ', '-'),
1068 self.__pat['firstnames'].replace(' ', '_'),
1069 self.__pat.get_formatted_dob(format = '%Y-%m-%d')
1070 )
1071
1072 f = codecs.open(filename = filename, mode = 'w+b', encoding = 'cp437', errors='replace')
1073 status = self.__export(target = f, encounter = encounter, soap_cats = soap_cats)
1074 f.close()
1075
1076 if export_to_import_file:
1077 # detect "LW:\medistar\inst\soap.txt"
1078 medistar_found = False
1079 for drive in u'cdefghijklmnopqrstuvwxyz':
1080 path = drive + ':\\medistar\\inst'
1081 if not os.path.isdir(path):
1082 continue
1083 try:
1084 import_fname = path + '\\soap.txt'
1085 open(import_fname, mode = 'w+b').close()
1086 _log.debug('exporting narrative to [%s] for Medistar import', import_fname)
1087 shutil.copyfile(filename, import_fname)
1088 medistar_found = True
1089 except IOError:
1090 continue
1091
1092 if not medistar_found:
1093 _log.debug('no Medistar installation found (no <LW>:\\medistar\\inst\\)')
1094
1095 return (status, filename)
1096 #--------------------------------------------------------
1099 #--------------------------------------------------------
1100 # interal API
1101 #--------------------------------------------------------
1103 # get data
1104 cmd = u"select narrative from clin.v_emr_journal where pk_patient=%s and pk_encounter=%s and soap_cat=%s"
1105 for soap_cat in soap_cats:
1106 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': (self.__pat['pk_identity'], encounter['pk_encounter'], soap_cat)}])
1107 target.write('*MD%s*\r\n' % gmClinNarrative.soap_cat2l10n[soap_cat])
1108 for row in rows:
1109 text = row[0]
1110 if text is None:
1111 continue
1112 target.write('%s\r\n' % gmTools.wrap (
1113 text = text,
1114 width = 64,
1115 eol = u'\r\n'
1116 ))
1117 return True
1118 #============================================================
1119 # main
1120 #------------------------------------------------------------
1122 """
1123 Prints application usage options to stdout.
1124 """
1125 print 'usage: python gmPatientExporter [--fileout=<outputfilename>] [--conf-file=<file>] [--text-domain=<textdomain>]'
1126 sys.exit(0)
1127 #------------------------------------------------------------
1129 """
1130 Main module application execution loop.
1131 """
1132 # More variable initializations
1133 patient = None
1134 patient_id = None
1135 patient_term = None
1136 pat_searcher = gmPersonSearch.cPatientSearcher_SQL()
1137
1138 # App execution loop
1139 while patient_term != 'bye':
1140 patient = gmPersonSearch.ask_for_patient()
1141 if patient is None:
1142 break
1143 # FIXME: needed ?
1144 # gmPerson.set_active_patient(patient=patient)
1145 exporter = cEMRJournalExporter()
1146 exporter.export_to_file(patient=patient)
1147 # export_tool.set_patient(patient)
1148 # Dump patient EMR sections
1149 # export_tool.dump_constraints()
1150 # export_tool.dump_demographic_record(True)
1151 # export_tool.dump_clinical_record()
1152 # export_tool.dump_med_docs()
1153
1154 # Clean ups
1155 # outFile.close()
1156 # export_tool.cleanup()
1157 if patient is not None:
1158 try:
1159 patient.cleanup()
1160 except:
1161 print "error cleaning up patient"
1162 #============================================================
1163 # main
1164 #------------------------------------------------------------
1165 if __name__ == "__main__":
1166 gmI18N.activate_locale()
1167 gmI18N.install_domain()
1168
1169 #--------------------------------------------------------
1171
1172 print "Exporting EMR journal(s) ..."
1173 pat_searcher = gmPersonSearch.cPatientSearcher_SQL()
1174 while True:
1175 patient = gmPersonSearch.ask_for_patient()
1176 if patient is None:
1177 break
1178
1179 exporter = cEMRJournalExporter()
1180 print "exported into file:", exporter.export_to_file(patient=patient)
1181
1182 if patient is not None:
1183 try:
1184 patient.cleanup()
1185 except:
1186 print "error cleaning up patient"
1187 print "Done."
1188 #--------------------------------------------------------
1189 print "\n\nGNUmed ASCII EMR Export"
1190 print "======================="
1191
1192 # run main loop
1193 export_journal()
1194
1195 #============================================================
1196 # $Log: gmPatientExporter.py,v $
1197 # Revision 1.138 2009-09-08 17:14:55 ncq
1198 # - apply unwrap() to encounter node title
1199 #
1200 # Revision 1.137 2009/07/15 12:47:25 ncq
1201 # - properly use patient age
1202 #
1203 # Revision 1.136 2009/06/29 15:01:07 ncq
1204 # - use get-latest-soap in encounter formatting
1205 #
1206 # Revision 1.135 2009/06/04 16:24:35 ncq
1207 # - support dob-less persons
1208 #
1209 # Revision 1.134 2009/01/02 11:36:43 ncq
1210 # - cleanup
1211 #
1212 # Revision 1.133 2008/12/18 21:26:45 ncq
1213 # - missing H in HH24 in date formatting in journal exporter
1214 #
1215 # Revision 1.132 2008/12/09 23:24:29 ncq
1216 # - .date -> .clin_when in doc_med
1217 #
1218 # Revision 1.131 2008/12/01 12:36:57 ncq
1219 # - improved encounter node label
1220 #
1221 # Revision 1.130 2008/10/22 12:06:05 ncq
1222 # - use %x in strftime
1223 #
1224 # Revision 1.129 2008/10/12 15:32:18 ncq
1225 # - support "mod date" in journal
1226 #
1227 # Revision 1.128 2008/09/02 18:59:30 ncq
1228 # - no more fk_patient in clin.health_issue and related changes
1229 #
1230 # Revision 1.127 2008/07/28 15:42:30 ncq
1231 # - cleanup
1232 # - enhance medistar exporter
1233 #
1234 # Revision 1.126 2008/07/12 15:20:55 ncq
1235 # - comment out print
1236 #
1237 # Revision 1.125 2008/07/07 13:39:22 ncq
1238 # - properly sort tree
1239 # - current patient .connected
1240 #
1241 # Revision 1.124 2008/06/24 13:55:14 ncq
1242 # - change encounter node label
1243 #
1244 # Revision 1.123 2008/06/23 09:59:57 ncq
1245 # - much improved journal layout
1246 #
1247 # Revision 1.122 2008/06/15 20:16:02 ncq
1248 # - add a space
1249 #
1250 # Revision 1.121 2008/05/19 15:44:16 ncq
1251 # - just silly cleanup
1252 #
1253 # Revision 1.120 2008/05/07 15:16:01 ncq
1254 # - use centralized soap category translations from gmClinNarrative
1255 #
1256 # Revision 1.119 2008/04/11 12:21:11 ncq
1257 # - some cleanup
1258 #
1259 # Revision 1.118 2008/04/02 10:15:54 ncq
1260 # - show local time zone in encounter summary
1261 #
1262 # Revision 1.117 2008/03/06 18:24:45 ncq
1263 # - indentation fix
1264 #
1265 # Revision 1.116 2008/03/05 22:25:09 ncq
1266 # - no more gmLog
1267 #
1268 # Revision 1.115 2008/01/30 13:46:17 ncq
1269 # - cleanup
1270 #
1271 # Revision 1.114 2008/01/22 11:52:24 ncq
1272 # - Unattributed
1273 # - improved Journal formatting as per list
1274 #
1275 # Revision 1.113 2008/01/13 01:13:58 ncq
1276 # - use issue.age_noted_human_readable()
1277 #
1278 # Revision 1.112 2008/01/11 16:10:00 ncq
1279 # - first/last -> first-/lastnames
1280 #
1281 # Revision 1.111 2007/12/26 22:26:04 shilbert
1282 # - indentation error fixed
1283 #
1284 # Revision 1.110 2007/12/23 11:56:38 ncq
1285 # - improve output, cleanup
1286 #
1287 # Revision 1.109 2007/11/28 11:52:13 ncq
1288 # - get_all_names() -> get_names()
1289 #
1290 # Revision 1.108 2007/11/05 12:10:05 ncq
1291 # - support admin soap type
1292 #
1293 # Revision 1.107 2007/06/18 19:33:19 ncq
1294 # - cleanup
1295 # - show date in narrative, too
1296 #
1297 # Revision 1.106 2007/05/21 14:46:44 ncq
1298 # - use patient['dirname']
1299 #
1300 # Revision 1.105 2007/05/14 13:09:04 ncq
1301 # - use bold on health issues with open episodes
1302 #
1303 # Revision 1.104 2007/04/01 15:25:55 ncq
1304 # - safely get encoding
1305 #
1306 # Revision 1.103 2007/03/02 15:30:00 ncq
1307 # - decode() strftime() output
1308 #
1309 # Revision 1.102 2007/02/22 17:30:48 ncq
1310 # - no more get_identity()
1311 # - patient now cIdentity() child
1312 #
1313 # Revision 1.101 2007/02/19 17:54:06 ncq
1314 # - need to return True when successful
1315 #
1316 # Revision 1.100 2007/02/19 16:56:05 ncq
1317 # - properly check for is_connected()
1318 #
1319 # Revision 1.99 2007/02/19 14:07:31 ncq
1320 # - fix format string parameters
1321 #
1322 # Revision 1.98 2007/02/17 14:10:03 ncq
1323 # - use improved gmTools.coalesce()
1324 #
1325 # Revision 1.97 2007/02/10 23:41:38 ncq
1326 # - fix loading of GNUmed python modules
1327 # - cleaned up journal exporter
1328 # - fixed bug in journal exporter where it expected is_connected()
1329 # in non-gmCurrentPatient-using context, too
1330 # - when run standalone: export journal
1331 #
1332 # Revision 1.96 2007/01/18 22:03:58 ncq
1333 # - a bit of cleanup
1334 #
1335 # Revision 1.95 2007/01/15 20:20:03 ncq
1336 # - move wrap() to gmTools
1337 #
1338 # Revision 1.94 2007/01/13 22:17:40 ncq
1339 # - wrap narrative to 75 characters per line
1340 #
1341 # Revision 1.93 2006/12/13 00:31:24 ncq
1342 # - export into unicode files
1343 # - fix use of get_encounters()
1344 #
1345 # Revision 1.92 2006/11/26 15:44:34 ncq
1346 # - strftime() does not accept u''
1347 #
1348 # Revision 1.91 2006/11/24 14:16:20 ncq
1349 # - unicode-robustify dump_item_fields()
1350 #
1351 # Revision 1.90 2006/11/19 11:05:38 ncq
1352 # - cleanup
1353 #
1354 # Revision 1.89 2006/11/09 17:48:05 ncq
1355 # - ever more careful handling of NULLs
1356 #
1357 # Revision 1.88 2006/11/07 00:25:19 ncq
1358 # - make journal exporter emit strictly u''
1359 #
1360 # Revision 1.87 2006/11/05 17:54:17 ncq
1361 # - don't use issue pk in get_encounters()
1362 # - gmPG -> gmPG2
1363 #
1364 # Revision 1.86 2006/11/05 17:02:54 ncq
1365 # - comment out lab results access, not in use yet
1366 #
1367 # Revision 1.85 2006/10/25 07:46:44 ncq
1368 # - Format() -> strftime() since datetime.datetime does not have .Format()
1369 #
1370 # Revision 1.84 2006/10/25 07:18:12 ncq
1371 # - no more gmPG
1372 #
1373 # Revision 1.83 2006/10/23 13:21:50 ncq
1374 # - vaccs/path lab not yet converted to gmPG2
1375 #
1376 # Revision 1.82 2006/09/03 14:46:26 ncq
1377 # - robustify regarding encoding issues
1378 # - improve test suite
1379 #
1380 # Revision 1.81 2006/07/19 20:25:48 ncq
1381 # - gmPyCompat.py is history
1382 #
1383 # Revision 1.80 2006/06/09 14:39:23 ncq
1384 # - comment out vaccination handling for now
1385 #
1386 # Revision 1.79 2006/05/30 13:36:35 ncq
1387 # - properly use get_encounters()
1388 #
1389 # Revision 1.78 2006/05/04 09:49:20 ncq
1390 # - get_clinical_record() -> get_emr()
1391 # - adjust to changes in set_active_patient()
1392 # - need explicit set_active_patient() after ask_for_patient() if wanted
1393 #
1394 # Revision 1.77 2006/02/27 22:38:36 ncq
1395 # - spell out rfe/aoe as per Richard's request
1396 #
1397 # Revision 1.76 2005/12/25 13:24:30 sjtan
1398 #
1399 # schema changes in names .
1400 #
1401 # Revision 1.75 2005/12/10 23:02:05 ncq
1402 # - tables are in clin.* now
1403 #
1404 # Revision 1.74 2005/10/30 15:48:56 ncq
1405 # - slightly enlarge space for provider signum display
1406 #
1407 # Revision 1.73 2005/10/19 09:06:39 ncq
1408 # - resolve merge conflict: just whitespace diff
1409 #
1410 # Revision 1.72 2005/10/18 13:34:01 sjtan
1411 # after running; small diffs
1412 #
1413 # Revision 1.71 2005/10/15 18:16:24 ncq
1414 # - encounter['description'] is gone, use 'aoe'
1415 #
1416 # Revision 1.70 2005/10/11 21:51:07 ncq
1417 # - rfe/aoe handling changes so adapt to that
1418 #
1419 # Revision 1.69 2005/10/08 12:33:09 sjtan
1420 # tree can be updated now without refetching entire cache; done by passing emr object to create_xxxx methods and calling emr.update_cache(key,obj);refresh_historical_tree non-destructively checks for changes and removes removed nodes and adds them if cache mismatch.
1421 #
1422 # Revision 1.68 2005/10/04 19:22:37 sjtan
1423 # allow a refetch of part of the cache, so that don't have to completely collapse tree view to view after change.
1424 #
1425 # Revision 1.67 2005/10/04 00:04:45 sjtan
1426 # convert to wx.; catch some transitional errors temporarily
1427 #
1428 # Revision 1.66 2005/10/03 13:49:21 sjtan
1429 # using new wx. temporary debugging to stdout(easier to read). where is rfe ?
1430 #
1431 # Revision 1.65 2005/09/11 17:28:20 ncq
1432 # - tree widget now display provider sign, not database account
1433 #
1434 # Revision 1.64 2005/09/09 13:50:07 ncq
1435 # - detail improvements in tree widget progress note output
1436 #
1437 # Revision 1.63 2005/09/08 16:57:06 ncq
1438 # - improve progress note display in tree widget
1439 #
1440 # Revision 1.62 2005/09/05 15:56:27 ncq
1441 # - sort journal by episode within encounters
1442 #
1443 # Revision 1.61 2005/09/04 07:28:51 ncq
1444 # - better naming of dummy health issue for unassociated episodes
1445 # - display time of entry in front of SOAP notes
1446 #
1447 # Revision 1.60 2005/07/04 11:14:36 ncq
1448 # - improved episode summary yet again
1449 #
1450 # Revision 1.59 2005/07/02 18:18:26 ncq
1451 # - improve EMR tree right side info pane according to user
1452 # testing and ideas gleaned from TransHIS
1453 #
1454 # Revision 1.58 2005/06/30 16:11:55 cfmoro
1455 # Bug fix: multiple episode w/o issue when refreshing tree
1456 #
1457 # Revision 1.57 2005/06/30 11:42:05 cfmoro
1458 # Removed debug print
1459 #
1460 # Revision 1.56 2005/06/30 11:30:10 cfmoro
1461 # Minor fix on issue info when no encounters attached
1462 #
1463 # Revision 1.55 2005/06/20 13:03:38 cfmoro
1464 # Relink encounter to another episode
1465 #
1466 # Revision 1.54 2005/06/12 22:09:39 ncq
1467 # - better encounter formatting yet
1468 #
1469 # Revision 1.53 2005/06/07 09:04:45 ncq
1470 # - cleanup, better encounter data display
1471 #
1472 # Revision 1.52 2005/05/17 18:11:41 ncq
1473 # - dob2medical_age is in gmPerson
1474 #
1475 # Revision 1.51 2005/05/12 15:08:31 ncq
1476 # - add Medistar SOAP exporter and wrap(text, width) convenience function
1477 #
1478 # Revision 1.50 2005/04/27 19:59:19 ncq
1479 # - deal with narrative rows that are empty
1480 #
1481 # Revision 1.49 2005/04/12 16:15:36 ncq
1482 # - improve journal style exporter
1483 #
1484 # Revision 1.48 2005/04/12 10:00:19 ncq
1485 # - add cEMRJournalExporter class
1486 #
1487 # Revision 1.47 2005/04/03 20:08:18 ncq
1488 # - GUI stuff does not belong here (eg move to gmEMRBrowser which is to become gmEMRWidgets, eventually)
1489 #
1490 # Revision 1.46 2005/04/03 09:27:25 ncq
1491 # - better wording
1492 #
1493 # Revision 1.45 2005/04/02 21:37:27 cfmoro
1494 # Unlinked episodes displayes in EMR tree and dump
1495 #
1496 # Revision 1.44 2005/04/02 20:45:12 cfmoro
1497 # Implementated exporting emr from gui client
1498 #
1499 # Revision 1.43 2005/03/30 21:14:31 cfmoro
1500 # Using cIdentity recent changes
1501 #
1502 # Revision 1.42 2005/03/29 07:24:07 ncq
1503 # - tabify
1504 #
1505 # Revision 1.41 2005/03/20 17:48:38 ncq
1506 # - add two sanity checks by Syan
1507 #
1508 # Revision 1.40 2005/02/20 08:32:51 sjtan
1509 #
1510 # indentation syntax error.
1511 #
1512 # Revision 1.39 2005/02/03 20:19:16 ncq
1513 # - get_demographic_record() -> get_identity()
1514 #
1515 # Revision 1.38 2005/01/31 13:01:23 ncq
1516 # - use ask_for_patient() in gmPerson
1517 #
1518 # Revision 1.37 2005/01/31 10:19:11 ncq
1519 # - gmPatient -> gmPerson
1520 #
1521 # Revision 1.36 2004/10/26 12:52:56 ncq
1522 # - Carlos: fix conceptual bug by building top-down (eg. issue -> episode
1523 # -> item) instead of bottom-up
1524 #
1525 # Revision 1.35 2004/10/20 21:43:45 ncq
1526 # - cleanup
1527 # - use allergy['descriptor']
1528 # - Format() dates
1529 #
1530 # Revision 1.34 2004/10/20 11:14:55 sjtan
1531 # restored import for unix. get_historical_tree may of changed, but mainly should
1532 # be guards in gmClinicalRecord for changing [] to None when functions expecting None, and client
1533 # functions passing [].
1534 #
1535 # Revision 1.33 2004/10/12 10:52:40 ncq
1536 # - improve vaccinations handling
1537 #
1538 # Revision 1.32 2004/10/11 19:53:41 ncq
1539 # - document classes are now VOs
1540 #
1541 # Revision 1.31 2004/09/29 19:13:37 ncq
1542 # - cosmetical fixes as discussed with our office staff
1543 #
1544 # Revision 1.30 2004/09/29 10:12:50 ncq
1545 # - Carlos added intuitive vaccination table - muchos improvos !
1546 #
1547 # Revision 1.29 2004/09/10 10:39:01 ncq
1548 # - fixed assignment that needs to be comparison == in lambda form
1549 #
1550 # Revision 1.28 2004/09/06 18:55:09 ncq
1551 # - a bunch of cleanups re get_historical_tree()
1552 #
1553 # Revision 1.27 2004/09/01 21:59:01 ncq
1554 # - python classes can only have one single __init__
1555 # - add in Carlos' code for displaying episode/issue summaries
1556 #
1557 # Revision 1.26 2004/08/23 09:08:53 ncq
1558 # - improve output
1559 #
1560 # Revision 1.25 2004/08/11 09:45:28 ncq
1561 # - format SOAP notes, too
1562 #
1563 # Revision 1.24 2004/08/09 18:41:08 ncq
1564 # - improved ASCII dump
1565 #
1566 # Revision 1.23 2004/07/26 00:02:30 ncq
1567 # - Carlos introduces export of RFE/AOE and dynamic layouting (left margin)
1568 #
1569 # Revision 1.22 2004/07/18 10:46:30 ncq
1570 # - lots of cleanup by Carlos
1571 #
1572 # Revision 1.21 2004/07/09 22:39:40 ncq
1573 # - write to file like object passed to __init__
1574 #
1575 # Revision 1.20 2004/07/06 00:26:06 ncq
1576 # - fail on _cfg is_instance of cNull(), not on missing conf-file option
1577 #
1578 # Revision 1.19 2004/07/03 17:15:59 ncq
1579 # - decouple contraint/patient/outfile handling
1580 #
1581 # Revision 1.18 2004/07/02 00:54:04 ncq
1582 # - constraints passing cleanup by Carlos
1583 #
1584 # Revision 1.17 2004/06/30 12:52:36 ncq
1585 # - cleanup
1586 #
1587 # Revision 1.16 2004/06/30 12:43:10 ncq
1588 # - read opts from config file, cleanup
1589 #
1590 # Revision 1.15 2004/06/29 08:16:35 ncq
1591 # - take output file from command line
1592 # - *search* for patients, don't require knowledge of their ID
1593 #
1594 # Revision 1.14 2004/06/28 16:15:56 ncq
1595 # - still more faulty id_ found
1596 #
1597 # Revision 1.13 2004/06/28 15:52:00 ncq
1598 # - some comments
1599 #
1600 # Revision 1.12 2004/06/28 12:18:52 ncq
1601 # - more id_* -> fk_*
1602 #
1603 # Revision 1.11 2004/06/26 23:45:50 ncq
1604 # - cleanup, id_* -> fk/pk_*
1605 #
1606 # Revision 1.10 2004/06/26 06:53:25 ncq
1607 # - id_episode -> pk_episode
1608 # - constrained by date range from Carlos
1609 # - dump documents folder, too, by Carlos
1610 #
1611 # Revision 1.9 2004/06/23 22:06:48 ncq
1612 # - cleaner error handling
1613 # - fit for further work by Carlos on UI interface/dumping to file
1614 # - nice stuff !
1615 #
1616 # Revision 1.8 2004/06/20 18:50:53 ncq
1617 # - some exception catching, needs more cleanup
1618 #
1619 # Revision 1.7 2004/06/20 18:35:07 ncq
1620 # - more work from Carlos
1621 #
1622 # Revision 1.6 2004/05/12 14:34:41 ncq
1623 # - now displays nice vaccination tables
1624 # - work by Carlos Moro
1625 #
1626 # Revision 1.5 2004/04/27 18:54:54 ncq
1627 # - adapt to gmClinicalRecord
1628 #
1629 # Revision 1.4 2004/04/24 13:35:33 ncq
1630 # - vacc table update
1631 #
1632 # Revision 1.3 2004/04/24 12:57:30 ncq
1633 # - stop db listeners on exit
1634 #
1635 # Revision 1.2 2004/04/20 13:00:22 ncq
1636 # - recent changes by Carlos to use VO API
1637 #
1638 # Revision 1.1 2004/03/25 23:10:02 ncq
1639 # - gmEmrExport -> gmPatientExporter by Carlos' suggestion
1640 #
1641 # Revision 1.2 2004/03/25 09:53:30 ncq
1642 # - added log keyword
1643 #
1644
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Nov 29 04:04:40 2010 | http://epydoc.sourceforge.net |