| 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: /cvsroot/gnumed/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, gmMedDoc, gmDemographicRecord, gmClinNarrative
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 parts = doc.get_parts()
838 for part in parts:
839 self.__target.write('\n %s - %s' % (
840 part['seq_idx'],
841 part['obj_comment'])
842 )
843 self.__target.write('\n\n')
844 #--------------------------------------------------------
846 """
847 Dumps in ASCII format some basic patient's demographic data
848 """
849 if self.__patient is None:
850 _log.error('cannot get Demographic export')
851 print(_(
852 'An error occurred while Demographic record export\n'
853 'Please check the log file for details.'
854 ))
855 return None
856
857 self.__target.write('\n\n\nDemographics')
858 self.__target.write('\n------------\n')
859 self.__target.write(' Id: %s \n' % self.__patient['pk_identity'])
860 cont = 0
861 for name in self.__patient.get_names():
862 if cont == 0:
863 self.__target.write(' Name (Active): %s, %s\n' % (name['firstnames'], name['lastnames']) )
864 else:
865 self.__target.write(' Name %s: %s, %s\n' % (cont, name['firstnames'], name['lastnames']))
866 cont += 1
867 self.__target.write(' Gender: %s\n' % self.__patient['gender'])
868 self.__target.write(' Title: %s\n' % self.__patient['title'])
869 self.__target.write(' Dob: %s\n' % self.__patient.get_formatted_dob(format = '%Y-%m-%d'))
870 self.__target.write(' Medical age: %s\n' % self.__patient.get_medical_age())
871 #--------------------------------------------------------
873 """
874 Dumps exporter filtering constraints
875 """
876 self.__first_constraint = True
877 if not self.__constraints['since'] is None:
878 self.dump_constraints_header()
879 self.__target.write('\nSince: %s' % self.__constraints['since'].strftime('%Y-%m-%d'))
880
881 if not self.__constraints['until'] is None:
882 self.dump_constraints_header()
883 self.__target.write('\nUntil: %s' % self.__constraints['until'].strftime('%Y-%m-%d'))
884
885 if not self.__constraints['encounters'] is None:
886 self.dump_constraints_header()
887 self.__target.write('\nEncounters: ')
888 for enc in self.__constraints['encounters']:
889 self.__target.write(str(enc) + ' ')
890
891 if not self.__constraints['episodes'] is None:
892 self.dump_constraints_header()
893 self.__target.write('\nEpisodes: ')
894 for epi in self.__constraints['episodes']:
895 self.__target.write(str(epi) + ' ')
896
897 if not self.__constraints['issues'] is None:
898 self.dump_constraints_header()
899 self.__target.write('\nIssues: ')
900 for iss in self.__constraints['issues']:
901 self.__target.write(str(iss) + ' ')
902 #--------------------------------------------------------
911 #============================================================
913 """Exports patient EMR into a simple chronological journal.
914
915 Note that this export will emit u'' strings only.
916 """
919 #--------------------------------------------------------
920 # external API
921 #--------------------------------------------------------
923 """Export medical record into a file.
924
925 @type filename: None (creates filename by itself) or string
926 @type patient: None (use currently active patient) or <gmPerson.cIdentity> instance
927 """
928 if patient is None:
929 patient = gmPerson.gmCurrentPatient()
930 if not patient.connected:
931 raise ValueError('[%s].export_to_file(): no active patient' % self.__class__.__name__)
932
933 if filename is None:
934 filename = u'%s-%s-%s-%s.txt' % (
935 _('emr-journal'),
936 patient['lastnames'].replace(u' ', u'_'),
937 patient['firstnames'].replace(u' ', u'_'),
938 patient.get_formatted_dob(format = '%Y-%m-%d')
939 )
940 path = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', patient['dirname'], filename))
941
942 f = codecs.open(filename = filename, mode = 'w+b', encoding = 'utf8', errors = 'replace')
943 self.export(target = f, patient = patient)
944 f.close()
945 return filename
946 #--------------------------------------------------------
947 # internal API
948 #--------------------------------------------------------
950 """
951 Export medical record into a Python object.
952
953 @type target: a python object supporting the write() API
954 @type patient: None (use currently active patient) or <gmPerson.cIdentity> instance
955 """
956 if patient is None:
957 patient = gmPerson.gmCurrentPatient()
958 if not patient.connected:
959 raise ValueError('[%s].export(): no active patient' % self.__class__.__name__)
960
961 # write header
962 txt = _('Chronological EMR Journal\n')
963 target.write(txt)
964 target.write(u'=' * (len(txt)-1))
965 target.write('\n')
966 target.write(_('Patient: %s (%s), No: %s\n') % (patient['description'], patient['gender'], patient['pk_identity']))
967 target.write(_('Born : %s, age: %s\n\n') % (
968 patient.get_formatted_dob(format = '%x', encoding = gmI18N.get_encoding()),
969 patient.get_medical_age()
970 ))
971 target.write(u'.-%10.10s---%9.9s-------%72.72s\n' % (u'-' * 10, u'-' * 9, u'-' * self.__part_len))
972 target.write(u'| %10.10s | %9.9s | | %s\n' % (_('Happened'), _('Doc'), _('Narrative')))
973 target.write(u'|-%10.10s---%9.9s-------%72.72s\n' % (u'-' * 10, u'-' * 9, u'-' * self.__part_len))
974
975 # get data
976 cmd = u"""
977 select
978 to_char(vemrj.clin_when, 'YYYY-MM-DD') as date,
979 vemrj.*,
980 (select rank from clin.soap_cat_ranks where soap_cat = vemrj.soap_cat) as scr,
981 to_char(vemrj.modified_when, 'YYYY-MM-DD HH24:MI') as date_modified
982 from clin.v_emr_journal vemrj
983 where pk_patient = %s
984 order by date, pk_episode, scr, src_table"""
985 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [patient['pk_identity']]}], get_col_idx = True)
986
987 # write data
988 prev_date = u''
989 prev_doc = u''
990 prev_soap = u''
991 for row in rows:
992 # narrative
993 if row['narrative'] is None:
994 continue
995
996 txt = gmTools.wrap (
997 text = row['narrative'].replace(u'\r', u'') + (u' (%s)' % row['date_modified']),
998 width = self.__part_len
999 ).split('\n')
1000
1001 # same provider ?
1002 curr_doc = row['modified_by']
1003 if curr_doc != prev_doc:
1004 prev_doc = curr_doc
1005 else:
1006 curr_doc = u''
1007
1008 # same soap category ?
1009 curr_soap = row['soap_cat']
1010 if curr_soap != prev_soap:
1011 prev_soap = curr_soap
1012
1013 # same date ?
1014 curr_date = row['date']
1015 if curr_date != prev_date:
1016 prev_date = curr_date
1017 curr_doc = row['modified_by']
1018 prev_doc = curr_doc
1019 curr_soap = row['soap_cat']
1020 prev_soap = curr_soap
1021 else:
1022 curr_date = u''
1023
1024 # display first part
1025 target.write(u'| %10.10s | %9.9s | %3.3s | %s\n' % (
1026 curr_date,
1027 curr_doc,
1028 gmClinNarrative.soap_cat2l10n[curr_soap],
1029 txt[0]
1030 ))
1031
1032 # only one part ?
1033 if len(txt) == 1:
1034 continue
1035
1036 template = u'| %10.10s | %9.9s | %3.3s | %s\n'
1037 for part in txt[1:]:
1038 line = template % (u'', u'', u' ', part)
1039 target.write(line)
1040
1041 # write footer
1042 target.write(u'`-%10.10s---%9.9s-------%72.72s\n\n' % (u'-' * 10, u'-' * 9, u'-' * self.__part_len))
1043 target.write(_('Exported: %s\n') % pyDT.datetime.now().strftime('%c').decode(gmI18N.get_encoding()))
1044
1045 return
1046 #============================================================
1048 """Export SOAP data per encounter into Medistar import format."""
1050 if patient is None:
1051 self.__pat = gmPerson.gmCurrentPatient()
1052 else:
1053 if not isinstance(patient, gmPerson.cIdentity):
1054 raise gmExceptions.ConstructorError, '<patient> argument must be instance of <cIdentity>, but is: %s' % type(patient)
1055 self.__pat = patient
1056 #--------------------------------------------------------
1057 # external API
1058 #--------------------------------------------------------
1059 - def export_to_file(self, filename=None, encounter=None, soap_cats=u'soap', export_to_import_file=False):
1060 if not self.__pat.connected:
1061 return (False, 'no active patient')
1062
1063 if filename is None:
1064 path = os.path.abspath(os.path.expanduser('~/gnumed/export'))
1065 filename = '%s-%s-%s-%s-%s.txt' % (
1066 os.path.join(path, 'Medistar-MD'),
1067 time.strftime('%Y-%m-%d',time.localtime()),
1068 self.__pat['lastnames'].replace(' ', '-'),
1069 self.__pat['firstnames'].replace(' ', '_'),
1070 self.__pat.get_formatted_dob(format = '%Y-%m-%d')
1071 )
1072
1073 f = codecs.open(filename = filename, mode = 'w+b', encoding = 'cp437', errors='replace')
1074 status = self.__export(target = f, encounter = encounter, soap_cats = soap_cats)
1075 f.close()
1076
1077 if export_to_import_file:
1078 # detect "LW:\medistar\inst\soap.txt"
1079 medistar_found = False
1080 for drive in u'cdefghijklmnopqrstuvwxyz':
1081 path = drive + ':\\medistar\\inst'
1082 if not os.path.isdir(path):
1083 continue
1084 try:
1085 import_fname = path + '\\soap.txt'
1086 open(import_fname, mode = 'w+b').close()
1087 _log.debug('exporting narrative to [%s] for Medistar import', import_fname)
1088 shutil.copyfile(filename, import_fname)
1089 medistar_found = True
1090 except IOError:
1091 continue
1092
1093 if not medistar_found:
1094 _log.debug('no Medistar installation found (no <LW>:\\medistar\\inst\\)')
1095
1096 return (status, filename)
1097 #--------------------------------------------------------
1100 #--------------------------------------------------------
1101 # interal API
1102 #--------------------------------------------------------
1104 # get data
1105 cmd = u"select narrative from clin.v_emr_journal where pk_patient=%s and pk_encounter=%s and soap_cat=%s"
1106 for soap_cat in soap_cats:
1107 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': (self.__pat['pk_identity'], encounter['pk_encounter'], soap_cat)}])
1108 target.write('*MD%s*\r\n' % gmClinNarrative.soap_cat2l10n[soap_cat])
1109 for row in rows:
1110 text = row[0]
1111 if text is None:
1112 continue
1113 target.write('%s\r\n' % gmTools.wrap (
1114 text = text,
1115 width = 64,
1116 eol = u'\r\n'
1117 ))
1118 return True
1119 #============================================================
1120 # main
1121 #------------------------------------------------------------
1123 """
1124 Prints application usage options to stdout.
1125 """
1126 print 'usage: python gmPatientExporter [--fileout=<outputfilename>] [--conf-file=<file>] [--text-domain=<textdomain>]'
1127 sys.exit(0)
1128 #------------------------------------------------------------
1130 """
1131 Main module application execution loop.
1132 """
1133 # More variable initializations
1134 patient = None
1135 patient_id = None
1136 patient_term = None
1137 pat_searcher = gmPerson.cPatientSearcher_SQL()
1138
1139 # App execution loop
1140 while patient_term != 'bye':
1141 patient = gmPerson.ask_for_patient()
1142 if patient is None:
1143 break
1144 # FIXME: needed ?
1145 # gmPerson.set_active_patient(patient=patient)
1146 exporter = cEMRJournalExporter()
1147 exporter.export_to_file(patient=patient)
1148 # export_tool.set_patient(patient)
1149 # Dump patient EMR sections
1150 # export_tool.dump_constraints()
1151 # export_tool.dump_demographic_record(True)
1152 # export_tool.dump_clinical_record()
1153 # export_tool.dump_med_docs()
1154
1155 # Clean ups
1156 # outFile.close()
1157 # export_tool.cleanup()
1158 if patient is not None:
1159 try:
1160 patient.cleanup()
1161 except:
1162 print "error cleaning up patient"
1163 #============================================================
1164 # main
1165 #------------------------------------------------------------
1166 if __name__ == "__main__":
1167 gmI18N.activate_locale()
1168 gmI18N.install_domain()
1169
1170 #--------------------------------------------------------
1172
1173 print "Exporting EMR journal(s) ..."
1174 pat_searcher = gmPerson.cPatientSearcher_SQL()
1175 while True:
1176 patient = gmPerson.ask_for_patient()
1177 if patient is None:
1178 break
1179
1180 exporter = cEMRJournalExporter()
1181 print "exported into file:", exporter.export_to_file(patient=patient)
1182
1183 if patient is not None:
1184 try:
1185 patient.cleanup()
1186 except:
1187 print "error cleaning up patient"
1188 print "Done."
1189 #--------------------------------------------------------
1190 print "\n\nGNUmed ASCII EMR Export"
1191 print "======================="
1192
1193 # run main loop
1194 export_journal()
1195
1196 #============================================================
1197 # $Log: gmPatientExporter.py,v $
1198 # Revision 1.138 2009/09/08 17:14:55 ncq
1199 # - apply unwrap() to encounter node title
1200 #
1201 # Revision 1.137 2009/07/15 12:47:25 ncq
1202 # - properly use patient age
1203 #
1204 # Revision 1.136 2009/06/29 15:01:07 ncq
1205 # - use get-latest-soap in encounter formatting
1206 #
1207 # Revision 1.135 2009/06/04 16:24:35 ncq
1208 # - support dob-less persons
1209 #
1210 # Revision 1.134 2009/01/02 11:36:43 ncq
1211 # - cleanup
1212 #
1213 # Revision 1.133 2008/12/18 21:26:45 ncq
1214 # - missing H in HH24 in date formatting in journal exporter
1215 #
1216 # Revision 1.132 2008/12/09 23:24:29 ncq
1217 # - .date -> .clin_when in doc_med
1218 #
1219 # Revision 1.131 2008/12/01 12:36:57 ncq
1220 # - improved encounter node label
1221 #
1222 # Revision 1.130 2008/10/22 12:06:05 ncq
1223 # - use %x in strftime
1224 #
1225 # Revision 1.129 2008/10/12 15:32:18 ncq
1226 # - support "mod date" in journal
1227 #
1228 # Revision 1.128 2008/09/02 18:59:30 ncq
1229 # - no more fk_patient in clin.health_issue and related changes
1230 #
1231 # Revision 1.127 2008/07/28 15:42:30 ncq
1232 # - cleanup
1233 # - enhance medistar exporter
1234 #
1235 # Revision 1.126 2008/07/12 15:20:55 ncq
1236 # - comment out print
1237 #
1238 # Revision 1.125 2008/07/07 13:39:22 ncq
1239 # - properly sort tree
1240 # - current patient .connected
1241 #
1242 # Revision 1.124 2008/06/24 13:55:14 ncq
1243 # - change encounter node label
1244 #
1245 # Revision 1.123 2008/06/23 09:59:57 ncq
1246 # - much improved journal layout
1247 #
1248 # Revision 1.122 2008/06/15 20:16:02 ncq
1249 # - add a space
1250 #
1251 # Revision 1.121 2008/05/19 15:44:16 ncq
1252 # - just silly cleanup
1253 #
1254 # Revision 1.120 2008/05/07 15:16:01 ncq
1255 # - use centralized soap category translations from gmClinNarrative
1256 #
1257 # Revision 1.119 2008/04/11 12:21:11 ncq
1258 # - some cleanup
1259 #
1260 # Revision 1.118 2008/04/02 10:15:54 ncq
1261 # - show local time zone in encounter summary
1262 #
1263 # Revision 1.117 2008/03/06 18:24:45 ncq
1264 # - indentation fix
1265 #
1266 # Revision 1.116 2008/03/05 22:25:09 ncq
1267 # - no more gmLog
1268 #
1269 # Revision 1.115 2008/01/30 13:46:17 ncq
1270 # - cleanup
1271 #
1272 # Revision 1.114 2008/01/22 11:52:24 ncq
1273 # - Unattributed
1274 # - improved Journal formatting as per list
1275 #
1276 # Revision 1.113 2008/01/13 01:13:58 ncq
1277 # - use issue.age_noted_human_readable()
1278 #
1279 # Revision 1.112 2008/01/11 16:10:00 ncq
1280 # - first/last -> first-/lastnames
1281 #
1282 # Revision 1.111 2007/12/26 22:26:04 shilbert
1283 # - indentation error fixed
1284 #
1285 # Revision 1.110 2007/12/23 11:56:38 ncq
1286 # - improve output, cleanup
1287 #
1288 # Revision 1.109 2007/11/28 11:52:13 ncq
1289 # - get_all_names() -> get_names()
1290 #
1291 # Revision 1.108 2007/11/05 12:10:05 ncq
1292 # - support admin soap type
1293 #
1294 # Revision 1.107 2007/06/18 19:33:19 ncq
1295 # - cleanup
1296 # - show date in narrative, too
1297 #
1298 # Revision 1.106 2007/05/21 14:46:44 ncq
1299 # - use patient['dirname']
1300 #
1301 # Revision 1.105 2007/05/14 13:09:04 ncq
1302 # - use bold on health issues with open episodes
1303 #
1304 # Revision 1.104 2007/04/01 15:25:55 ncq
1305 # - safely get encoding
1306 #
1307 # Revision 1.103 2007/03/02 15:30:00 ncq
1308 # - decode() strftime() output
1309 #
1310 # Revision 1.102 2007/02/22 17:30:48 ncq
1311 # - no more get_identity()
1312 # - patient now cIdentity() child
1313 #
1314 # Revision 1.101 2007/02/19 17:54:06 ncq
1315 # - need to return True when successful
1316 #
1317 # Revision 1.100 2007/02/19 16:56:05 ncq
1318 # - properly check for is_connected()
1319 #
1320 # Revision 1.99 2007/02/19 14:07:31 ncq
1321 # - fix format string parameters
1322 #
1323 # Revision 1.98 2007/02/17 14:10:03 ncq
1324 # - use improved gmTools.coalesce()
1325 #
1326 # Revision 1.97 2007/02/10 23:41:38 ncq
1327 # - fix loading of GNUmed python modules
1328 # - cleaned up journal exporter
1329 # - fixed bug in journal exporter where it expected is_connected()
1330 # in non-gmCurrentPatient-using context, too
1331 # - when run standalone: export journal
1332 #
1333 # Revision 1.96 2007/01/18 22:03:58 ncq
1334 # - a bit of cleanup
1335 #
1336 # Revision 1.95 2007/01/15 20:20:03 ncq
1337 # - move wrap() to gmTools
1338 #
1339 # Revision 1.94 2007/01/13 22:17:40 ncq
1340 # - wrap narrative to 75 characters per line
1341 #
1342 # Revision 1.93 2006/12/13 00:31:24 ncq
1343 # - export into unicode files
1344 # - fix use of get_encounters()
1345 #
1346 # Revision 1.92 2006/11/26 15:44:34 ncq
1347 # - strftime() does not accept u''
1348 #
1349 # Revision 1.91 2006/11/24 14:16:20 ncq
1350 # - unicode-robustify dump_item_fields()
1351 #
1352 # Revision 1.90 2006/11/19 11:05:38 ncq
1353 # - cleanup
1354 #
1355 # Revision 1.89 2006/11/09 17:48:05 ncq
1356 # - ever more careful handling of NULLs
1357 #
1358 # Revision 1.88 2006/11/07 00:25:19 ncq
1359 # - make journal exporter emit strictly u''
1360 #
1361 # Revision 1.87 2006/11/05 17:54:17 ncq
1362 # - don't use issue pk in get_encounters()
1363 # - gmPG -> gmPG2
1364 #
1365 # Revision 1.86 2006/11/05 17:02:54 ncq
1366 # - comment out lab results access, not in use yet
1367 #
1368 # Revision 1.85 2006/10/25 07:46:44 ncq
1369 # - Format() -> strftime() since datetime.datetime does not have .Format()
1370 #
1371 # Revision 1.84 2006/10/25 07:18:12 ncq
1372 # - no more gmPG
1373 #
1374 # Revision 1.83 2006/10/23 13:21:50 ncq
1375 # - vaccs/path lab not yet converted to gmPG2
1376 #
1377 # Revision 1.82 2006/09/03 14:46:26 ncq
1378 # - robustify regarding encoding issues
1379 # - improve test suite
1380 #
1381 # Revision 1.81 2006/07/19 20:25:48 ncq
1382 # - gmPyCompat.py is history
1383 #
1384 # Revision 1.80 2006/06/09 14:39:23 ncq
1385 # - comment out vaccination handling for now
1386 #
1387 # Revision 1.79 2006/05/30 13:36:35 ncq
1388 # - properly use get_encounters()
1389 #
1390 # Revision 1.78 2006/05/04 09:49:20 ncq
1391 # - get_clinical_record() -> get_emr()
1392 # - adjust to changes in set_active_patient()
1393 # - need explicit set_active_patient() after ask_for_patient() if wanted
1394 #
1395 # Revision 1.77 2006/02/27 22:38:36 ncq
1396 # - spell out rfe/aoe as per Richard's request
1397 #
1398 # Revision 1.76 2005/12/25 13:24:30 sjtan
1399 #
1400 # schema changes in names .
1401 #
1402 # Revision 1.75 2005/12/10 23:02:05 ncq
1403 # - tables are in clin.* now
1404 #
1405 # Revision 1.74 2005/10/30 15:48:56 ncq
1406 # - slightly enlarge space for provider signum display
1407 #
1408 # Revision 1.73 2005/10/19 09:06:39 ncq
1409 # - resolve merge conflict: just whitespace diff
1410 #
1411 # Revision 1.72 2005/10/18 13:34:01 sjtan
1412 # after running; small diffs
1413 #
1414 # Revision 1.71 2005/10/15 18:16:24 ncq
1415 # - encounter['description'] is gone, use 'aoe'
1416 #
1417 # Revision 1.70 2005/10/11 21:51:07 ncq
1418 # - rfe/aoe handling changes so adapt to that
1419 #
1420 # Revision 1.69 2005/10/08 12:33:09 sjtan
1421 # 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.
1422 #
1423 # Revision 1.68 2005/10/04 19:22:37 sjtan
1424 # allow a refetch of part of the cache, so that don't have to completely collapse tree view to view after change.
1425 #
1426 # Revision 1.67 2005/10/04 00:04:45 sjtan
1427 # convert to wx.; catch some transitional errors temporarily
1428 #
1429 # Revision 1.66 2005/10/03 13:49:21 sjtan
1430 # using new wx. temporary debugging to stdout(easier to read). where is rfe ?
1431 #
1432 # Revision 1.65 2005/09/11 17:28:20 ncq
1433 # - tree widget now display provider sign, not database account
1434 #
1435 # Revision 1.64 2005/09/09 13:50:07 ncq
1436 # - detail improvements in tree widget progress note output
1437 #
1438 # Revision 1.63 2005/09/08 16:57:06 ncq
1439 # - improve progress note display in tree widget
1440 #
1441 # Revision 1.62 2005/09/05 15:56:27 ncq
1442 # - sort journal by episode within encounters
1443 #
1444 # Revision 1.61 2005/09/04 07:28:51 ncq
1445 # - better naming of dummy health issue for unassociated episodes
1446 # - display time of entry in front of SOAP notes
1447 #
1448 # Revision 1.60 2005/07/04 11:14:36 ncq
1449 # - improved episode summary yet again
1450 #
1451 # Revision 1.59 2005/07/02 18:18:26 ncq
1452 # - improve EMR tree right side info pane according to user
1453 # testing and ideas gleaned from TransHIS
1454 #
1455 # Revision 1.58 2005/06/30 16:11:55 cfmoro
1456 # Bug fix: multiple episode w/o issue when refreshing tree
1457 #
1458 # Revision 1.57 2005/06/30 11:42:05 cfmoro
1459 # Removed debug print
1460 #
1461 # Revision 1.56 2005/06/30 11:30:10 cfmoro
1462 # Minor fix on issue info when no encounters attached
1463 #
1464 # Revision 1.55 2005/06/20 13:03:38 cfmoro
1465 # Relink encounter to another episode
1466 #
1467 # Revision 1.54 2005/06/12 22:09:39 ncq
1468 # - better encounter formatting yet
1469 #
1470 # Revision 1.53 2005/06/07 09:04:45 ncq
1471 # - cleanup, better encounter data display
1472 #
1473 # Revision 1.52 2005/05/17 18:11:41 ncq
1474 # - dob2medical_age is in gmPerson
1475 #
1476 # Revision 1.51 2005/05/12 15:08:31 ncq
1477 # - add Medistar SOAP exporter and wrap(text, width) convenience function
1478 #
1479 # Revision 1.50 2005/04/27 19:59:19 ncq
1480 # - deal with narrative rows that are empty
1481 #
1482 # Revision 1.49 2005/04/12 16:15:36 ncq
1483 # - improve journal style exporter
1484 #
1485 # Revision 1.48 2005/04/12 10:00:19 ncq
1486 # - add cEMRJournalExporter class
1487 #
1488 # Revision 1.47 2005/04/03 20:08:18 ncq
1489 # - GUI stuff does not belong here (eg move to gmEMRBrowser which is to become gmEMRWidgets, eventually)
1490 #
1491 # Revision 1.46 2005/04/03 09:27:25 ncq
1492 # - better wording
1493 #
1494 # Revision 1.45 2005/04/02 21:37:27 cfmoro
1495 # Unlinked episodes displayes in EMR tree and dump
1496 #
1497 # Revision 1.44 2005/04/02 20:45:12 cfmoro
1498 # Implementated exporting emr from gui client
1499 #
1500 # Revision 1.43 2005/03/30 21:14:31 cfmoro
1501 # Using cIdentity recent changes
1502 #
1503 # Revision 1.42 2005/03/29 07:24:07 ncq
1504 # - tabify
1505 #
1506 # Revision 1.41 2005/03/20 17:48:38 ncq
1507 # - add two sanity checks by Syan
1508 #
1509 # Revision 1.40 2005/02/20 08:32:51 sjtan
1510 #
1511 # indentation syntax error.
1512 #
1513 # Revision 1.39 2005/02/03 20:19:16 ncq
1514 # - get_demographic_record() -> get_identity()
1515 #
1516 # Revision 1.38 2005/01/31 13:01:23 ncq
1517 # - use ask_for_patient() in gmPerson
1518 #
1519 # Revision 1.37 2005/01/31 10:19:11 ncq
1520 # - gmPatient -> gmPerson
1521 #
1522 # Revision 1.36 2004/10/26 12:52:56 ncq
1523 # - Carlos: fix conceptual bug by building top-down (eg. issue -> episode
1524 # -> item) instead of bottom-up
1525 #
1526 # Revision 1.35 2004/10/20 21:43:45 ncq
1527 # - cleanup
1528 # - use allergy['descriptor']
1529 # - Format() dates
1530 #
1531 # Revision 1.34 2004/10/20 11:14:55 sjtan
1532 # restored import for unix. get_historical_tree may of changed, but mainly should
1533 # be guards in gmClinicalRecord for changing [] to None when functions expecting None, and client
1534 # functions passing [].
1535 #
1536 # Revision 1.33 2004/10/12 10:52:40 ncq
1537 # - improve vaccinations handling
1538 #
1539 # Revision 1.32 2004/10/11 19:53:41 ncq
1540 # - document classes are now VOs
1541 #
1542 # Revision 1.31 2004/09/29 19:13:37 ncq
1543 # - cosmetical fixes as discussed with our office staff
1544 #
1545 # Revision 1.30 2004/09/29 10:12:50 ncq
1546 # - Carlos added intuitive vaccination table - muchos improvos !
1547 #
1548 # Revision 1.29 2004/09/10 10:39:01 ncq
1549 # - fixed assignment that needs to be comparison == in lambda form
1550 #
1551 # Revision 1.28 2004/09/06 18:55:09 ncq
1552 # - a bunch of cleanups re get_historical_tree()
1553 #
1554 # Revision 1.27 2004/09/01 21:59:01 ncq
1555 # - python classes can only have one single __init__
1556 # - add in Carlos' code for displaying episode/issue summaries
1557 #
1558 # Revision 1.26 2004/08/23 09:08:53 ncq
1559 # - improve output
1560 #
1561 # Revision 1.25 2004/08/11 09:45:28 ncq
1562 # - format SOAP notes, too
1563 #
1564 # Revision 1.24 2004/08/09 18:41:08 ncq
1565 # - improved ASCII dump
1566 #
1567 # Revision 1.23 2004/07/26 00:02:30 ncq
1568 # - Carlos introduces export of RFE/AOE and dynamic layouting (left margin)
1569 #
1570 # Revision 1.22 2004/07/18 10:46:30 ncq
1571 # - lots of cleanup by Carlos
1572 #
1573 # Revision 1.21 2004/07/09 22:39:40 ncq
1574 # - write to file like object passed to __init__
1575 #
1576 # Revision 1.20 2004/07/06 00:26:06 ncq
1577 # - fail on _cfg is_instance of cNull(), not on missing conf-file option
1578 #
1579 # Revision 1.19 2004/07/03 17:15:59 ncq
1580 # - decouple contraint/patient/outfile handling
1581 #
1582 # Revision 1.18 2004/07/02 00:54:04 ncq
1583 # - constraints passing cleanup by Carlos
1584 #
1585 # Revision 1.17 2004/06/30 12:52:36 ncq
1586 # - cleanup
1587 #
1588 # Revision 1.16 2004/06/30 12:43:10 ncq
1589 # - read opts from config file, cleanup
1590 #
1591 # Revision 1.15 2004/06/29 08:16:35 ncq
1592 # - take output file from command line
1593 # - *search* for patients, don't require knowledge of their ID
1594 #
1595 # Revision 1.14 2004/06/28 16:15:56 ncq
1596 # - still more faulty id_ found
1597 #
1598 # Revision 1.13 2004/06/28 15:52:00 ncq
1599 # - some comments
1600 #
1601 # Revision 1.12 2004/06/28 12:18:52 ncq
1602 # - more id_* -> fk_*
1603 #
1604 # Revision 1.11 2004/06/26 23:45:50 ncq
1605 # - cleanup, id_* -> fk/pk_*
1606 #
1607 # Revision 1.10 2004/06/26 06:53:25 ncq
1608 # - id_episode -> pk_episode
1609 # - constrained by date range from Carlos
1610 # - dump documents folder, too, by Carlos
1611 #
1612 # Revision 1.9 2004/06/23 22:06:48 ncq
1613 # - cleaner error handling
1614 # - fit for further work by Carlos on UI interface/dumping to file
1615 # - nice stuff !
1616 #
1617 # Revision 1.8 2004/06/20 18:50:53 ncq
1618 # - some exception catching, needs more cleanup
1619 #
1620 # Revision 1.7 2004/06/20 18:35:07 ncq
1621 # - more work from Carlos
1622 #
1623 # Revision 1.6 2004/05/12 14:34:41 ncq
1624 # - now displays nice vaccination tables
1625 # - work by Carlos Moro
1626 #
1627 # Revision 1.5 2004/04/27 18:54:54 ncq
1628 # - adapt to gmClinicalRecord
1629 #
1630 # Revision 1.4 2004/04/24 13:35:33 ncq
1631 # - vacc table update
1632 #
1633 # Revision 1.3 2004/04/24 12:57:30 ncq
1634 # - stop db listeners on exit
1635 #
1636 # Revision 1.2 2004/04/20 13:00:22 ncq
1637 # - recent changes by Carlos to use VO API
1638 #
1639 # Revision 1.1 2004/03/25 23:10:02 ncq
1640 # - gmEmrExport -> gmPatientExporter by Carlos' suggestion
1641 #
1642 # Revision 1.2 2004/03/25 09:53:30 ncq
1643 # - added log keyword
1644 #
1645
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Feb 9 04:02:05 2010 | http://epydoc.sourceforge.net |