| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed medication/substances handling widgets.
2 """
3 #================================================================
4 __version__ = "$Revision: 1.33 $"
5 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
6
7 import logging, sys, os.path, webbrowser
8
9
10 import wx, wx.grid
11
12
13 if __name__ == '__main__':
14 sys.path.insert(0, '../../')
15 from Gnumed.pycommon import gmDispatcher, gmCfg, gmShellAPI, gmTools, gmDateTime
16 from Gnumed.pycommon import gmMatchProvider, gmI18N, gmPrinting, gmCfg2
17 from Gnumed.business import gmPerson, gmATC, gmSurgery, gmMedication, gmForms
18 from Gnumed.wxpython import gmGuiHelpers, gmRegetMixin, gmAuthWidgets, gmEditArea, gmMacro
19 from Gnumed.wxpython import gmCfgWidgets, gmListWidgets, gmPhraseWheel, gmFormWidgets
20 from Gnumed.wxpython import gmAllergyWidgets
21
22
23 _log = logging.getLogger('gm.ui')
24 _log.info(__version__)
25
26 #============================================================
27 # ATC related widgets
28 #============================================================
29
31
32 if parent is None:
33 parent = wx.GetApp().GetTopWindow()
34 #------------------------------------------------------------
35 def refresh(lctrl):
36 atcs = gmATC.get_reference_atcs()
37
38 items = [ [
39 a['atc'],
40 a['term'],
41 u'%s' % gmTools.coalesce(a['ddd'], u''),
42 gmTools.coalesce(a['unit'], u''),
43 gmTools.coalesce(a['administrative_route'], u''),
44 gmTools.coalesce(a['comment'], u''),
45 a['version'],
46 a['lang']
47 ] for a in atcs ]
48 lctrl.set_string_items(items)
49 lctrl.set_data(atcs)
50 #------------------------------------------------------------
51 gmListWidgets.get_choices_from_list (
52 parent = parent,
53 msg = _('\nThe ATC codes as known to GNUmed.\n'),
54 caption = _('Showing ATC codes.'),
55 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
56 single_selection = True,
57 refresh_callback = refresh
58 )
59
60 #============================================================
61
63
64 dlg = wx.FileDialog (
65 parent = None,
66 message = _('Choose an ATC import config file'),
67 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
68 defaultFile = '',
69 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
70 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
71 )
72
73 result = dlg.ShowModal()
74 if result == wx.ID_CANCEL:
75 return
76
77 cfg_file = dlg.GetPath()
78 dlg.Destroy()
79
80 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
81 if conn is None:
82 return False
83
84 wx.BeginBusyCursor()
85
86 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
87 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
88 else:
89 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
90
91 wx.EndBusyCursor()
92 return True
93
94 #============================================================
95
97
99
100 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
101
102 query = u"""
103
104 SELECT DISTINCT ON (label)
105 atc_code,
106 label
107 FROM (
108
109 SELECT
110 code as atc_code,
111 (code || ': ' || term || coalesce(' (' || ddd || unit || ')', ''))
112 AS label
113 FROM ref.atc
114 WHERE
115 term %(fragment_condition)s
116 OR
117 code %(fragment_condition)s
118
119 UNION ALL
120
121 SELECT
122 atc_code,
123 (atc_code || ': ' || description)
124 AS label
125 FROM ref.substance_in_brand
126 WHERE
127 description %(fragment_condition)s
128 OR
129 atc_code %(fragment_condition)s
130
131 UNION ALL
132
133 SELECT
134 atc_code,
135 (atc_code || ': ' || description || ' (' || preparation || ')')
136 AS label
137 FROM ref.branded_drug
138 WHERE
139 description %(fragment_condition)s
140 OR
141 atc_code %(fragment_condition)s
142
143 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL
144
145 ) AS candidates
146
147 ORDER BY label
148 LIMIT 50"""
149
150 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
151 mp.setThresholds(1, 2, 4)
152 # mp.word_separators = '[ \t=+&:@]+'
153 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.'))
154 self.matcher = mp
155 self.selection_only = True
156
157 #============================================================
158 #============================================================
159
161
162 if parent is None:
163 parent = wx.GetApp().GetTopWindow()
164
165 #------------------------------------------------------------
166 def delete(component):
167 gmMedication.delete_component_from_branded_drug (
168 brand = component['pk_brand'],
169 component = component['pk_substance_in_brand']
170 )
171 return True
172 #------------------------------------------------------------
173 def refresh(lctrl):
174 substs = gmMedication.get_substances_in_brands()
175 items = [ [
176 u'%s%s' % (s['brand'], gmTools.coalesce(s['atc_brand'], u'', u' (%s)')),
177 s['substance'],
178 gmTools.coalesce(s['atc_substance'], u''),
179 s['preparation'],
180 gmTools.coalesce(s['external_code_brand'], u'', u'%%s [%s]' % s['external_code_type_brand']),
181 s['pk_substance_in_brand']
182 ] for s in substs ]
183 lctrl.set_string_items(items)
184 lctrl.set_data(substs)
185 #------------------------------------------------------------
186 msg = _('\nThese are the substances in the drug brands known to GNUmed.\n')
187
188 gmListWidgets.get_choices_from_list (
189 parent = parent,
190 msg = msg,
191 caption = _('Showing drug brand components (substances).'),
192 columns = [_('Brand'), _('Substance'), u'ATC', _('Preparation'), _('Code'), u'#'],
193 single_selection = True,
194 #new_callback = new,
195 #edit_callback = edit,
196 delete_callback = delete,
197 refresh_callback = refresh
198 )
199 #============================================================
201
202 if parent is None:
203 parent = wx.GetApp().GetTopWindow()
204 #------------------------------------------------------------
205 def delete(brand):
206 if brand.is_vaccine:
207 gmGuiHelpers.gm_show_info (
208 aTitle = _('Deleting medication'),
209 aMessage = _(
210 'Cannot delete the medication\n'
211 '\n'
212 ' "%s" (%s)\n'
213 '\n'
214 'because it is a vaccine. Please delete it\n'
215 'from the vaccine management section !\n'
216 ) % (brand['description'], brand['preparation'])
217 )
218 return False
219 gmMedication.delete_branded_drug(brand = brand['pk'])
220 return True
221 #------------------------------------------------------------
222 def new():
223 drug_db = get_drug_database(parent = parent)
224
225 if drug_db is None:
226 return False
227
228 drug_db.import_drugs()
229
230 return True
231 #------------------------------------------------------------
232 def refresh(lctrl):
233 drugs = gmMedication.get_branded_drugs()
234 items = [ [
235 d['description'],
236 d['preparation'],
237 gmTools.coalesce(d['atc_code'], u''),
238 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
239 d['pk']
240 ] for d in drugs ]
241 lctrl.set_string_items(items)
242 lctrl.set_data(drugs)
243 #------------------------------------------------------------
244 msg = _('\nThese are the drug brands known to GNUmed.\n')
245
246 gmListWidgets.get_choices_from_list (
247 parent = parent,
248 msg = msg,
249 caption = _('Showing branded drugs.'),
250 columns = [_('Name'), _('Preparation'), _('ATC'), _('Code'), u'#'],
251 single_selection = True,
252 refresh_callback = refresh,
253 new_callback = new,
254 #edit_callback = edit,
255 delete_callback = delete
256 )
257 #============================================================
259
260 if parent is None:
261 parent = wx.GetApp().GetTopWindow()
262 #------------------------------------------------------------
263 def delete(substance):
264 gmMedication.delete_used_substance(substance = substance['pk'])
265 return True
266 #------------------------------------------------------------
267 # def new():
268 # drug_db = get_drug_database(parent = parent)
269 #
270 # if drug_db is None:
271 # return False
272 #
273 # drug_db.import_drugs()
274 #
275 # return True
276 #------------------------------------------------------------
277 def refresh(lctrl):
278 substs = gmMedication.get_substances_in_use()
279 items = [ [
280 s['description'],
281 gmTools.coalesce(s['atc_code'], u''),
282 s['pk']
283 ] for s in substs ]
284 lctrl.set_string_items(items)
285 lctrl.set_data(substs)
286 #------------------------------------------------------------
287 msg = _('Substances currently or previously consumed across all patients.')
288
289 gmListWidgets.get_choices_from_list (
290 parent = parent,
291 msg = msg,
292 caption = _('Showing consumed substances.'),
293 columns = [_('Name'), _('ATC'), u'#'],
294 single_selection = True,
295 refresh_callback = refresh,
296 #new_callback = new,
297 #edit_callback = edit,
298 delete_callback = delete
299 )
300 #============================================================
301 # generic drug database access
302 #============================================================
304 gmCfgWidgets.configure_string_from_list_option (
305 parent = parent,
306 message = _(
307 '\n'
308 'Please select the default drug data source from the list below.\n'
309 '\n'
310 'Note that to actually use it you need to have the database installed, too.'
311 ),
312 option = 'external.drug_data.default_source',
313 bias = 'user',
314 default_value = None,
315 choices = gmMedication.drug_data_source_interfaces.keys(),
316 columns = [_('Drug data source')],
317 data = gmMedication.drug_data_source_interfaces.keys(),
318 caption = _('Configuring default drug data source')
319 )
320 #============================================================
322 dbcfg = gmCfg.cCfgSQL()
323
324 default_db = dbcfg.get2 (
325 option = 'external.drug_data.default_source',
326 workplace = gmSurgery.gmCurrentPractice().active_workplace,
327 bias = 'workplace'
328 )
329
330 if default_db is None:
331 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True)
332 configure_drug_data_source(parent = parent)
333 default_db = dbcfg.get2 (
334 option = 'external.drug_data.default_source',
335 workplace = gmSurgery.gmCurrentPractice().active_workplace,
336 bias = 'workplace'
337 )
338 if default_db is None:
339 gmGuiHelpers.gm_show_error (
340 aMessage = _('There is no default drug database configured.'),
341 aTitle = _('Jumping to drug database')
342 )
343 return None
344
345 try:
346 return gmMedication.drug_data_source_interfaces[default_db]()
347 except KeyError:
348 _log.error('faulty default drug data source configuration: %s', default_db)
349 return None
350
351 #============================================================
353 dbcfg = gmCfg.cCfgSQL()
354 drug_db = get_drug_database()
355 if drug_db is None:
356 return
357 pat = gmPerson.gmCurrentPatient()
358 if pat.connected:
359 drug_db.patient = pat
360 drug_db.switch_to_frontend(blocking = False)
361
362 #============================================================
364
365 dbcfg = gmCfg.cCfgSQL()
366
367 ifap_cmd = dbcfg.get2 (
368 option = 'external.ifap-win.shell_command',
369 workplace = gmSurgery.gmCurrentPractice().active_workplace,
370 bias = 'workplace',
371 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
372 )
373 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
374 if not found:
375 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
376 return False
377 ifap_cmd = binary
378
379 if import_drugs:
380 transfer_file = os.path.expanduser(dbcfg.get2 (
381 option = 'external.ifap-win.transfer_file',
382 workplace = gmSurgery.gmCurrentPractice().active_workplace,
383 bias = 'workplace',
384 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
385 ))
386 # file must exist for Ifap to write into it
387 try:
388 f = open(transfer_file, 'w+b').close()
389 except IOError:
390 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
391 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
392 return False
393
394 wx.BeginBusyCursor()
395 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
396 wx.EndBusyCursor()
397
398 if import_drugs:
399 # COMMENT: this file must exist PRIOR to invoking IFAP
400 # COMMENT: or else IFAP will not write data into it ...
401 try:
402 csv_file = open(transfer_file, 'rb') # FIXME: encoding
403 except:
404 _log.exception('cannot access [%s]', fname)
405 csv_file = None
406
407 if csv_file is not None:
408 import csv
409 csv_lines = csv.DictReader (
410 csv_file,
411 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
412 delimiter = ';'
413 )
414 pat = gmPerson.gmCurrentPatient()
415 emr = pat.get_emr()
416 # dummy episode for now
417 epi = emr.add_episode(episode_name = _('Current medication'))
418 for line in csv_lines:
419 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
420 line['Packungszahl'].strip(),
421 line['Handelsname'].strip(),
422 line['Form'].strip(),
423 line[u'Packungsgr\xf6\xdfe'].strip(),
424 line['Abpackungsmenge'].strip(),
425 line['Einheit'].strip(),
426 line['Hersteller'].strip(),
427 line['PZN'].strip()
428 )
429 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
430 csv_file.close()
431
432 return True
433
434 #============================================================
435 # current substance intake handling
436 #============================================================
438
440
441 query = u"""
442 SELECT DISTINCT ON (sched)
443 schedule as sched,
444 schedule
445 FROM clin.substance_intake
446 WHERE schedule %(fragment_condition)s
447 ORDER BY sched
448 LIMIT 50"""
449
450 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
451 mp.setThresholds(1, 2, 4)
452 mp.word_separators = '[ \t=+&:@]+'
453 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
454 self.SetToolTipString(_('The schedule for taking this substance.'))
455 self.matcher = mp
456 self.selection_only = False
457 #============================================================
459
461
462 query = u"""
463 (
464 SELECT DISTINCT ON (preparation)
465 preparation as prep, preparation
466 FROM ref.branded_drug
467 WHERE preparation %(fragment_condition)s
468 ) UNION (
469 SELECT DISTINCT ON (preparation)
470 preparation as prep, preparation
471 FROM clin.substance_intake
472 WHERE preparation %(fragment_condition)s
473 )
474 ORDER BY prep
475 limit 30"""
476
477 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
478 mp.setThresholds(1, 2, 4)
479 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
480 self.SetToolTipString(_('The preparation (form) of the substance the patient is taking.'))
481 self.matcher = mp
482 self.selection_only = False
483 #============================================================
485
487
488 query = u"""
489 (
490 SELECT
491 pk::text,
492 description as subst
493 --(description || coalesce(' [' || atc_code || ']', '')) as subst
494 FROM clin.consumed_substance
495 WHERE description %(fragment_condition)s
496
497 ) UNION (
498
499 SELECT
500 description,
501 description as subst
502 --NULL,
503 --(description || coalesce(' [' || atc_code || ']', '')) as subst
504 FROM ref.substance_in_brand
505 WHERE description %(fragment_condition)s
506
507 ) UNION (
508
509 SELECT
510 term,
511 term as subst
512 --NULL,
513 --(term || ' [' || atc || ']') as subst
514 FROM ref.v_atc
515 WHERE
516 is_group_code IS FALSE
517 AND
518 term %(fragment_condition)s
519 )
520 ORDER BY subst
521 LIMIT 50"""
522
523 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
524 mp.setThresholds(1, 2, 4)
525 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
526 self.SetToolTipString(_('The INN / substance the patient is taking.'))
527 self.matcher = mp
528 self.selection_only = False
529 #---------------------------------------------------------
531
532 if self.data is not None:
533 try:
534 int(self.data)
535 except ValueError:
536 self.data = None
537
538 return super(cSubstancePhraseWheel, self).GetData(can_create = can_create, as_instance = as_instance)
539 #============================================================
541
543
544 query = u"""
545 SELECT
546 pk,
547 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
548 AS brand
549 FROM ref.branded_drug
550 WHERE description %(fragment_condition)s
551 ORDER BY brand
552 LIMIT 50"""
553
554 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
555 mp.setThresholds(2, 3, 4)
556 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
557 self.SetToolTipString(_('The brand name of the drug the patient is taking.'))
558 self.matcher = mp
559 self.selection_only = False
560
561 #============================================================
562 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
563
564 -class cCurrentMedicationEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
565
567
568 try:
569 data = kwargs['substance']
570 del kwargs['substance']
571 except KeyError:
572 data = None
573
574 wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl.__init__(self, *args, **kwargs)
575 gmEditArea.cGenericEditAreaMixin.__init__(self)
576 self.mode = 'new'
577 self.data = data
578 if data is not None:
579 self.mode = 'edit'
580
581 self.__init_ui()
582 #----------------------------------------------------------------
584
585 self._PRW_brand.add_callback_on_lose_focus(callback = self._on_leave_brand)
586 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance)
587 #----------------------------------------------------------------
589 emr = gmPerson.gmCurrentPatient().get_emr()
590
591 state = emr.allergy_state
592 if state['last_confirmed'] is None:
593 confirmed = _('never')
594 else:
595 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
596 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
597 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
598 msg += u'\n'
599
600 for allergy in emr.get_allergies():
601 msg += u'%s (%s, %s): %s\n' % (
602 allergy['descriptor'],
603 allergy['l10n_type'],
604 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'),
605 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
606 )
607
608 self._LBL_allergies.SetLabel(msg)
609 #----------------------------------------------------------------
611
612 if self._PRW_brand.GetData() is None:
613 self._TCTRL_brand_ingredients.SetValue(u'')
614 if self.data is None:
615 return
616 if self.data['pk_brand'] is None:
617 return
618 self._PRW_brand.SetText(self.data['brand'], self.data['pk_brand'])
619
620 brand = gmMedication.cBrandedDrug(aPK_obj = self._PRW_brand.GetData())
621
622 if self.data is None:
623 self._PRW_preparation.SetText(brand['preparation'], None)
624 else:
625 self._PRW_preparation.SetText (
626 gmTools.coalesce(self.data['preparation'], brand['preparation']),
627 self.data['preparation']
628 )
629
630 comps = brand.components
631
632 if comps is None:
633 return
634
635 if len(comps) == 0:
636 return
637
638 comps = u' / '.join([ u'%s%s' % (c['description'], gmTools.coalesce(c['atc_code'], u'', u' (%s)')) for c in comps ])
639 self._TCTRL_brand_ingredients.SetValue(comps)
640 #----------------------------------------------------------------
641 # generic Edit Area mixin API
642 #----------------------------------------------------------------
644
645 validity = True
646
647 has_brand = (self._PRW_brand.GetData() is not None)
648 has_substance = (self._PRW_substance.GetValue().strip() == u'')
649
650 # must have either brand or substance
651 if not (has_brand or has_substance):
652 self._PRW_substance.display_as_valid(False)
653 self._PRW_brand.display_as_valid(False)
654 validity = False
655 else:
656 self._PRW_substance.display_as_valid(True)
657 self._PRW_brand.display_as_valid(True)
658
659 # brands must have components or they cannot be used
660 if has_brand:
661 brand = gmMedication.cBrandedDrug(aPK_obj = self._PRW_brand.GetData())
662 if len(brand.components) == 0:
663 self._PRW_brand.display_as_valid(False)
664 validity = False
665
666 # brands already have a preparation, so only required for substances
667 if not has_brand:
668 if self._PRW_preparation.GetValue().strip() == u'':
669 self._PRW_preparation.display_as_valid(False)
670 validity = False
671 else:
672 self._PRW_preparation.display_as_valid(True)
673
674 # episode must be set if intake is to be approved of
675 if self._CHBOX_approved.IsChecked():
676 if self._PRW_episode.GetValue().strip() == u'':
677 self._PRW_episode.display_as_valid(False)
678 validity = False
679 else:
680 self._PRW_episode.display_as_valid(True)
681
682 # huh ?
683 if self._CHBOX_approved.IsChecked() is True:
684 self._PRW_duration.display_as_valid(True)
685 else:
686 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
687 self._PRW_duration.display_as_valid(True)
688 else:
689 if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None:
690 self._PRW_duration.display_as_valid(False)
691 validity = False
692 else:
693 self._PRW_duration.display_as_valid(True)
694
695 # end must be > start if at all
696 end = self._DP_discontinued.GetValue(as_pydt = True, invalid_as_none = True)
697 if end is not None:
698 start = self._DP_started.GetValue(as_pydt = True)
699 if start > end:
700 self._DP_started.display_as_valid(False)
701 self._DP_discontinued.display_as_valid(False)
702 validity = False
703 else:
704 self._DP_started.display_as_valid(True)
705 self._DP_discontinued.display_as_valid(True)
706
707 if validity is False:
708 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save substance intake. Invalid or missing essential input.'))
709
710 return validity
711 #----------------------------------------------------------------
713
714 pk_brand = self._PRW_brand.GetData()
715 brand = None
716
717 if pk_brand is None:
718 prep = self._PRW_preparation.GetValue()
719 if self._PRW_substance.GetData() is None:
720 subst = self._PRW_substance.GetValue().strip()
721 else:
722 # normalize, do not simply re-use name from phrasewheel
723 subst = gmMedication.get_substance_by_pk(pk = self._PRW_substance.GetData())['description']
724 substances = [
725 [subst['description'], self._PRW_strength.GetValue()]
726 ]
727 else:
728 brand = gmMedication.cBrandedDrug(aPK_obj = pk_brand)
729 prep = brand['preparation']
730 comps = brand.components
731 if len(comps) == 1:
732 substances = [
733 [comps[0], self._PRW_strength.GetValue()]
734 ]
735 else:
736 # loop and ask for strengths
737 print "missing"
738
739 emr = gmPerson.gmCurrentPatient().get_emr()
740 epi = self._PRW_episode.GetData(can_create = True)
741
742 # now loop over substances either from brand or from input
743 last_intake = None
744 for subst, strength in substances:
745 intake = emr.add_substance_intake (
746 substance = subst,
747 episode = epi,
748 preparation = prep
749 )
750 intake['strength'] = strength
751 intake['started'] = self._DP_started.GetValue(as_pydt = True, invalid_as_none = True)
752 intake['discontinued'] = self._DP_discontinued.GetValue(as_pydt = True, invalid_as_none = True)
753 if intake['discontinued'] is None:
754 intake['discontinue_reason'] = None
755 else:
756 intake['discontinue_reason'] = self._PRW_discontinue_reason().GetValue().strip()
757 intake['schedule'] = self._PRW_schedule.GetValue()
758 intake['aim'] = self._PRW_aim.GetValue()
759 intake['notes'] = self._PRW_notes.GetValue()
760 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
761 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
762 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
763 intake['duration'] = None
764 else:
765 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
766 intake['pk_brand'] = pk_brand
767 intake.save()
768 last_intake = intake
769
770 self.data = last_intake
771
772 if self._CHBOX_is_allergy.IsChecked():
773 if brand is None:
774 allg = self.data.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
775 else:
776 allg = brand.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
777 # open for editing
778 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1)
779 dlg.ShowModal()
780
781 return True
782
783 # # brand pre-selected ?
784 # if pk_brand is None:
785 # # no, so ...
786 # desc = self._PRW_brand.GetValue().strip()
787 # if desc != u'':
788 # # ... create or get it
789 # brand = gmMedication.create_branded_drug (
790 # brand_name = desc,
791 # preparation = self._PRW_preparation.GetValue().strip(),
792 # return_existing = True
793 # )
794 # pk_brand = brand['pk']
795 # else:
796 # # yes, so get it
797 # brand = gmMedication.cBrandedDrug(aPK_obj = pk_brand)
798 #
799 # # brand neither creatable nor pre-selected
800 # if brand is None:
801 # self.data = intake
802 # return True
803 #
804 # # 4) add substance to brand as component (because
805 # # that's effectively what we are saying here)
806 # # FIXME: we may want to ask the user here
807 # # FIXME: or only do it if there are no components yet
808 # if self._PRW_substance.GetData() is None:
809 # brand.add_component(substance = self._PRW_substance.GetValue().strip())
810 # else:
811 # # normalize substance name
812 # subst = gmMedication.get_substance_by_pk(pk = self._PRW_substance.GetData())
813 # if subst is not None:
814 # brand.add_component(substance = subst['description'])
815 #
816 #----------------------------------------------------------------
818
819 if self._PRW_substance.GetData() is None:
820 self.data['pk_substance'] = gmMedication.create_used_substance (
821 substance = self._PRW_substance.GetValue().strip()
822 )['pk']
823 else:
824 self.data['pk_substance'] = self._PRW_substance.GetData()
825
826 self.data['started'] = self._DP_started.GetValue(as_pydt = True, invalid_as_none = True)
827 self.data['discontinued'] = self._DP_discontinued.GetValue(as_pydt = True, invalid_as_none = True)
828 if self.data['discontinued'] is None:
829 self.data['discontinue_reason'] = None
830 else:
831 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
832 self.data['preparation'] = self._PRW_preparation.GetValue()
833 self.data['strength'] = self._PRW_strength.GetValue()
834 self.data['schedule'] = self._PRW_schedule.GetValue()
835 self.data['aim'] = self._PRW_aim.GetValue()
836 self.data['notes'] = self._PRW_notes.GetValue()
837 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
838 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
839 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
840
841 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
842 self.data['duration'] = None
843 else:
844 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
845
846 if self._PRW_brand.GetData() is None:
847 desc = self._PRW_brand.GetValue().strip()
848 if desc != u'':
849 # create or get brand
850 self.data['pk_brand'] = gmMedication.create_branded_drug (
851 brand_name = desc,
852 preparation = self._PRW_preparation.GetValue().strip(),
853 return_existing = True
854 )['pk']
855 else:
856 self.data['pk_brand'] = self._PRW_brand.GetData()
857
858 self.data.save()
859
860 if self._CHBOX_is_allergy.IsChecked():
861 allg = self.data.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
862 # open for editing
863 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1)
864 dlg.ShowModal()
865
866 return True
867 #----------------------------------------------------------------
869 self._PRW_brand.SetText(u'', None)
870 self._TCTRL_brand_ingredients.SetValue(u'')
871
872 self._PRW_substance.SetText(u'', None)
873 self._PRW_substance.Enable(True)
874
875 self._PRW_strength.SetText(u'', None)
876 self._PRW_strength.Enable(True)
877
878 self._PRW_preparation.SetText(u'', None)
879 self._PRW_preparation.Enable(True)
880
881 self._PRW_schedule.SetText(u'', None)
882 self._PRW_duration.SetText(u'', None)
883 self._PRW_aim.SetText(u'', None)
884 self._PRW_notes.SetText(u'', None)
885 self._PRW_episode.SetText(u'', None)
886
887 self._CHBOX_long_term.SetValue(False)
888 self._CHBOX_approved.SetValue(True)
889
890 self._DP_started.SetValue(gmDateTime.pydt_now_here())
891 self._DP_discontinued.SetValue(None)
892 self._PRW_discontinue_reason.SetValue(u'')
893
894 #self.__refresh_brand_and_components()
895 self.__refresh_allergies()
896
897 self._PRW_brand.SetFocus()
898 #----------------------------------------------------------------
900
901 self._PRW_substance.SetText(self.data['substance'], self.data['pk_substance'])
902 self._PRW_strength.SetText(gmTools.coalesce(self.data['strength'], u''), self.data['strength'])
903 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
904
905 if self.data['is_long_term']:
906 self._CHBOX_long_term.SetValue(True)
907 self._PRW_duration.Enable(False)
908 self._PRW_duration.SetText(gmTools.u_infinity, None)
909 self._BTN_discontinued_as_planned.Enable(False)
910 else:
911 self._CHBOX_long_term.SetValue(False)
912 self._PRW_duration.Enable(True)
913 self._BTN_discontinued_as_planned.Enable(True)
914 if self.data['duration'] is None:
915 self._PRW_duration.SetText(u'', None)
916 else:
917 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
918 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
919 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
920 self._PRW_episode.SetData(self.data['pk_episode'])
921 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
922
923 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
924
925 self._DP_started.SetValue(self.data['started'])
926 self._DP_discontinued.SetValue(self.data['discontinued'])
927 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
928
929 self.__refresh_brand_and_components()
930 self.__refresh_allergies()
931
932 self._PRW_substance.SetFocus()
933 #----------------------------------------------------------------
936 #----------------------------------------------------------------
937 # event handlers
938 #----------------------------------------------------------------
940 if self._PRW_brand.GetData() is None:
941 self._PRW_brand.SetText(u'', None)
942 self._LBL_substance.Enable(True)
943 self._PRW_substance.Enable(True)
944 self._BTN_database_substance.Enable(True)
945 self._LBL_strength.Enable(True)
946 self._PRW_strength.Enable(True)
947 self._LBL_preparation.Enable(True)
948 self._PRW_preparation.Enable(True)
949 self._PRW_preparation.SetText(u'', None)
950 self._TCTRL_brand_ingredients.SetValue(u'')
951 else:
952 brand = gmMedication.cBrandedDrug(aPK_obj = self._PRW_brand.GetData())
953 comps = brand.components
954
955 self._LBL_substance.Enable(False)
956 self._PRW_substance.Enable(False)
957 self._BTN_database_substance.Enable(False)
958 if len(comps) == 1:
959 self._LBL_strength.Enable(True)
960 self._PRW_strength.Enable(True)
961 else:
962 self._LBL_strength.Enable(False)
963 self._PRW_strength.Enable(False)
964 self._LBL_preparation.Enable(False)
965 self._PRW_preparation.Enable(False)
966 self._PRW_preparation.SetText(brand['preparation'], None)
967 self._TCTRL_brand_ingredients.SetValue(u' / '.join ([
968 u'%s%s' % (
969 c['description'],
970 gmTools.coalesce(c['atc_code'], u'', u' (%s)')
971 ) for c in comps
972 ]))
973 #----------------------------------------------------------------
975 if self._PRW_substance.GetValue().strip() == u'':
976 self._PRW_brand.Enable(True)
977 self._BTN_database_brand.Enable(True)
978
979 self._LBL_preparation.Enable(False)
980 self._PRW_preparation.Enable(False)
981 else:
982 self._PRW_brand.SetText(u'', None)
983 self._PRW_brand.Enable(False)
984 self._BTN_database_brand.Enable(False)
985 self._TCTRL_brand_ingredients.SetValue(u'')
986
987 self._LBL_strength.Enable(True)
988 self._PRW_strength.Enable(True)
989 self._LBL_preparation.Enable(True)
990 self._PRW_preparation.Enable(True)
991 self._PRW_preparation.SetText(u'', None)
992 #----------------------------------------------------------------
994 if self._DP_discontinued.GetValue() is None:
995 self._PRW_discontinue_reason.Enable(False)
996 self._CHBOX_is_allergy.Enable(False)
997 #self._LBL_reason.Enable(False)
998 else:
999 self._PRW_discontinue_reason.Enable(True)
1000 self._CHBOX_is_allergy.Enable(True)
1001 #self._LBL_reason.Enable(True)
1002 #----------------------------------------------------------------
1021 #----------------------------------------------------------------
1043 #----------------------------------------------------------------
1072 #----------------------------------------------------------------
1074 if self._CHBOX_long_term.IsChecked() is True:
1075 self._PRW_duration.Enable(False)
1076 self._BTN_discontinued_as_planned.Enable(False)
1077 self._PRW_discontinue_reason.Enable(False)
1078 self._CHBOX_is_allergy.Enable(False)
1079 else:
1080 self._PRW_duration.Enable(True)
1081 self._BTN_discontinued_as_planned.Enable(True)
1082 self._PRW_discontinue_reason.Enable(True)
1083 self._CHBOX_is_allergy.Enable(True)
1084
1085 self.__refresh_allergies()
1086 #----------------------------------------------------------------
1094 #============================================================
1096
1097 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1098 msg = _(
1099 '\n'
1100 '[%s]\n'
1101 '\n'
1102 'It may be prudent to edit (before deletion) the details\n'
1103 'of this substance intake entry so as to leave behind\n'
1104 'some indication of why it was deleted.\n'
1105 ) % subst.format()
1106
1107 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1108 parent,
1109 -1,
1110 caption = _('Deleting medication / substance intake'),
1111 question = msg,
1112 button_defs = [
1113 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1114 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1115 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1116 ]
1117 )
1118
1119 edit_first = dlg.ShowModal()
1120 dlg.Destroy()
1121
1122 if edit_first == wx.ID_CANCEL:
1123 return
1124
1125 if edit_first == wx.ID_YES:
1126 edit_intake_of_substance(parent = parent, substance = subst)
1127 delete_it = gmGuiHelpers.gm_show_question (
1128 aMessage = _('Now delete substance intake entry ?'),
1129 aTitle = _('Deleting medication / substance intake')
1130 )
1131 else:
1132 delete_it = True
1133
1134 if not delete_it:
1135 return
1136
1137 gmMedication.delete_substance_intake(substance = substance)
1138 #------------------------------------------------------------
1140 ea = cCurrentMedicationEAPnl(parent = parent, id = -1, substance = substance)
1141 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (substance is not None))
1142 dlg.SetTitle(gmTools.coalesce(substance, _('Adding substance intake'), _('Editing substance intake')))
1143 if dlg.ShowModal() == wx.ID_OK:
1144 dlg.Destroy()
1145 return True
1146 dlg.Destroy()
1147 return False
1148 #============================================================
1149 # current substances grid
1150 #------------------------------------------------------------
1152
1153 if parent is None:
1154 parent = wx.GetApp().GetTopWindow()
1155
1156 template = gmFormWidgets.manage_form_templates (
1157 parent = parent,
1158 template_types = ['current medication list']
1159 )
1160 option = u'form_templates.medication_list'
1161
1162 if template is None:
1163 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
1164 return None
1165
1166 if template['engine'] != u'L':
1167 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
1168 return None
1169
1170 dbcfg = gmCfg.cCfgSQL()
1171 dbcfg.set (
1172 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1173 option = option,
1174 value = u'%s - %s' % (template['name_long'], template['external_version'])
1175 )
1176
1177 return template
1178 #------------------------------------------------------------
1180
1181 if parent is None:
1182 parent = wx.GetApp().GetTopWindow()
1183
1184 # 1) get template
1185 dbcfg = gmCfg.cCfgSQL()
1186 option = u'form_templates.medication_list'
1187
1188 template = dbcfg.get2 (
1189 option = option,
1190 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1191 bias = 'user'
1192 )
1193
1194 if template is None:
1195 template = configure_medication_list_template(parent = parent)
1196 if template is None:
1197 gmGuiHelpers.gm_show_error (
1198 aMessage = _('There is no medication list template configured.'),
1199 aTitle = _('Printing medication list')
1200 )
1201 return False
1202 else:
1203 try:
1204 name, ver = template.split(u' - ')
1205 except:
1206 _log.exception('problem splitting medication list template name [%s]', template)
1207 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1208 return False
1209 template = gmForms.get_form_template(name_long = name, external_version = ver)
1210 if template is None:
1211 gmGuiHelpers.gm_show_error (
1212 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1213 aTitle = _('Printing medication list')
1214 )
1215 return False
1216
1217 # 2) process template
1218 try:
1219 meds_list = template.instantiate()
1220 except KeyError:
1221 _log.exception('cannot instantiate medication list template [%s]', template)
1222 gmGuiHelpers.gm_show_error (
1223 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
1224 aTitle = _('Printing medication list')
1225 )
1226 return False
1227
1228 ph = gmMacro.gmPlaceholderHandler()
1229 #ph.debug = True
1230 meds_list.substitute_placeholders(data_source = ph)
1231 pdf_name = meds_list.generate_output(cleanup = cleanup)
1232 if cleanup:
1233 meds_list.cleanup()
1234 if pdf_name is None:
1235 gmGuiHelpers.gm_show_error (
1236 aMessage = _('Error generating the medication list.'),
1237 aTitle = _('Printing medication list')
1238 )
1239 return False
1240
1241 # 3) print template
1242 printed = gmPrinting.print_file_by_shellscript(filename = pdf_name, jobtype = 'medication_list')
1243 if not printed:
1244 gmGuiHelpers.gm_show_error (
1245 aMessage = _('Error printing the medication list.'),
1246 aTitle = _('Printing medication list')
1247 )
1248 return False
1249
1250 pat = gmPerson.gmCurrentPatient()
1251 emr = pat.get_emr()
1252 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1253 emr.add_clin_narrative (
1254 soap_cat = None,
1255 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1256 episode = epi
1257 )
1258
1259 return True
1260 #------------------------------------------------------------
1262 """A grid class for displaying current substance intake.
1263
1264 - does NOT listen to the currently active patient
1265 - thereby it can display any patient at any time
1266 """
1268
1269 wx.grid.Grid.__init__(self, *args, **kwargs)
1270
1271 self.__patient = None
1272 self.__row_data = {}
1273 self.__prev_row = None
1274 self.__prev_tooltip_row = None
1275 self.__prev_cell_0 = None
1276 self.__grouping_mode = u'episode'
1277 self.__filter_show_unapproved = False
1278 self.__filter_show_inactive = False
1279
1280 self.__grouping2col_labels = {
1281 u'episode': [
1282 _('Episode'),
1283 _('Substance'),
1284 _('Dose'),
1285 _('Schedule'),
1286 _('Started'),
1287 _('Duration'),
1288 _('Brand')
1289 ],
1290 u'brand': [
1291 _('Brand'),
1292 _('Schedule'),
1293 _('Substance'),
1294 _('Dose'),
1295 _('Started'),
1296 _('Duration'),
1297 _('Episode')
1298 ]
1299 }
1300
1301 self.__grouping2order_by_clauses = {
1302 u'episode': u'pk_health_issue nulls first, episode, substance, started',
1303 u'brand': u'brand nulls last, substance, started'
1304 }
1305
1306 self.__init_ui()
1307 self.__register_events()
1308 #------------------------------------------------------------
1309 # external API
1310 #------------------------------------------------------------
1312
1313 sel_block_top_left = self.GetSelectionBlockTopLeft()
1314 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1315 sel_cols = self.GetSelectedCols()
1316 sel_rows = self.GetSelectedRows()
1317
1318 selected_cells = []
1319
1320 # individually selected cells (ctrl-click)
1321 selected_cells += self.GetSelectedCells()
1322
1323 # selected rows
1324 selected_cells += list (
1325 (row, col)
1326 for row in sel_rows
1327 for col in xrange(self.GetNumberCols())
1328 )
1329
1330 # selected columns
1331 selected_cells += list (
1332 (row, col)
1333 for row in xrange(self.GetNumberRows())
1334 for col in sel_cols
1335 )
1336
1337 # selection blocks
1338 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1339 selected_cells += [
1340 (row, col)
1341 for row in xrange(top_left[0], bottom_right[0] + 1)
1342 for col in xrange(top_left[1], bottom_right[1] + 1)
1343 ]
1344
1345 return set(selected_cells)
1346 #------------------------------------------------------------
1348 rows = {}
1349
1350 for row, col in self.get_selected_cells():
1351 rows[row] = True
1352
1353 return rows.keys()
1354 #------------------------------------------------------------
1356 return [ self.__row_data[row] for row in self.get_selected_rows() ]
1357 #------------------------------------------------------------
1359
1360 self.empty_grid()
1361
1362 if self.__patient is None:
1363 return
1364
1365 emr = self.__patient.get_emr()
1366 meds = emr.get_current_substance_intake (
1367 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
1368 include_unapproved = self.__filter_show_unapproved,
1369 include_inactive = self.__filter_show_inactive
1370 )
1371 if not meds:
1372 return
1373
1374 self.BeginBatch()
1375
1376 # columns
1377 labels = self.__grouping2col_labels[self.__grouping_mode]
1378 if self.__filter_show_unapproved:
1379 self.AppendCols(numCols = len(labels) + 1)
1380 else:
1381 self.AppendCols(numCols = len(labels))
1382 for col_idx in range(len(labels)):
1383 self.SetColLabelValue(col_idx, labels[col_idx])
1384 if self.__filter_show_unapproved:
1385 self.SetColLabelValue(len(labels), u'OK?')
1386 self.SetColSize(len(labels), 40)
1387
1388 self.AppendRows(numRows = len(meds))
1389
1390 # loop over data
1391 for row_idx in range(len(meds)):
1392 med = meds[row_idx]
1393 self.__row_data[row_idx] = med
1394
1395 if med['is_currently_active'] is True:
1396 atcs = []
1397 if med['atc_substance'] is not None:
1398 atcs.append(med['atc_substance'])
1399 # if med['atc_brand'] is not None:
1400 # atcs.append(med['atc_brand'])
1401 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],), brand = med['brand'])
1402 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
1403 if allg not in [None, False]:
1404 attr = self.GetOrCreateCellAttr(row_idx, 0)
1405 if allg['type'] == u'allergy':
1406 attr.SetTextColour('red')
1407 else:
1408 attr.SetTextColour('yellow')
1409 self.SetRowAttr(row_idx, attr)
1410 else:
1411 attr = self.GetOrCreateCellAttr(row_idx, 0)
1412 attr.SetTextColour('grey')
1413 self.SetRowAttr(row_idx, attr)
1414
1415 if self.__grouping_mode == u'episode':
1416 if med['pk_episode'] is None:
1417 self.__prev_cell_0 = None
1418 self.SetCellValue(row_idx, 0, gmTools.u_diameter)
1419 else:
1420 if self.__prev_cell_0 != med['episode']:
1421 self.__prev_cell_0 = med['episode']
1422 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['episode'], u''))
1423
1424 self.SetCellValue(row_idx, 1, med['substance'])
1425 self.SetCellValue(row_idx, 2, gmTools.coalesce(med['strength'], u''))
1426 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
1427 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1428
1429 if med['is_long_term']:
1430 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1431 else:
1432 if med['duration'] is None:
1433 self.SetCellValue(row_idx, 5, u'')
1434 else:
1435 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1436
1437 if med['pk_brand'] is None:
1438 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u''))
1439 else:
1440 if med['fake_brand']:
1441 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'', _('%s (fake)')))
1442 else:
1443 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u''))
1444
1445 elif self.__grouping_mode == u'brand':
1446
1447 if med['pk_brand'] is None:
1448 self.__prev_cell_0 = None
1449 self.SetCellValue(row_idx, 0, gmTools.u_diameter)
1450 else:
1451 if self.__prev_cell_0 != med['brand']:
1452 self.__prev_cell_0 = med['brand']
1453 if med['fake_brand']:
1454 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'', _('%s (fake)')))
1455 else:
1456 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u''))
1457
1458 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
1459 self.SetCellValue(row_idx, 2, med['substance'])
1460 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['strength'], u''))
1461 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1462
1463 if med['is_long_term']:
1464 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1465 else:
1466 if med['duration'] is None:
1467 self.SetCellValue(row_idx, 5, u'')
1468 else:
1469 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1470
1471 if med['pk_episode'] is None:
1472 self.SetCellValue(row_idx, 6, u'')
1473 else:
1474 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['episode'], u''))
1475
1476 else:
1477 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
1478
1479 if self.__filter_show_unapproved:
1480 self.SetCellValue (
1481 row_idx,
1482 len(labels),
1483 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
1484 )
1485
1486 #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1487
1488 self.EndBatch()
1489 #------------------------------------------------------------
1491 self.BeginBatch()
1492 self.ClearGrid()
1493 # Windows cannot do "nothing", it rather decides to assert()
1494 # on thinking it is supposed to do nothing
1495 if self.GetNumberRows() > 0:
1496 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
1497 if self.GetNumberCols() > 0:
1498 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
1499 self.EndBatch()
1500 self.__row_data = {}
1501 self.__prev_cell_0 = None
1502 #------------------------------------------------------------
1504
1505 if len(self.__row_data) == 0:
1506 return
1507
1508 sel_rows = self.get_selected_rows()
1509 if len(sel_rows) != 1:
1510 return
1511
1512 drug_db = get_drug_database()
1513 if drug_db is None:
1514 return
1515
1516 drug_db.show_info_on_substance(substance = self.get_selected_data()[0])
1517 #------------------------------------------------------------
1519
1520 if len(self.__row_data) == 0:
1521 return
1522
1523 sel_rows = self.get_selected_rows()
1524
1525 if len(sel_rows) != 1:
1526 return
1527
1528 webbrowser.open (
1529 url = gmMedication.drug2renal_insufficiency_url(search_term = self.get_selected_data()[0]),
1530 new = False,
1531 autoraise = True
1532 )
1533 #------------------------------------------------------------
1535
1536 dbcfg = gmCfg.cCfgSQL()
1537
1538 url = dbcfg.get2 (
1539 option = u'external.urls.report_ADR',
1540 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1541 bias = u'user',
1542 default = u'https://dcgma.org/uaw/meldung.php' # http://www.akdae.de/Arzneimittelsicherheit/UAW-Meldung/UAW-Meldung-online.html
1543 )
1544
1545 webbrowser.open(url = url, new = False, autoraise = True)
1546 #------------------------------------------------------------
1548
1549 if len(self.__row_data) == 0:
1550 return
1551
1552 drug_db = get_drug_database()
1553 if drug_db is None:
1554 return
1555
1556 if len(self.get_selected_rows()) > 1:
1557 drug_db.check_drug_interactions(substances = self.get_selected_data())
1558 else:
1559 drug_db.check_drug_interactions(substances = self.__row_data.values())
1560 #------------------------------------------------------------
1562 edit_intake_of_substance(parent = self, substance = None)
1563 #------------------------------------------------------------
1565
1566 rows = self.get_selected_rows()
1567
1568 if len(rows) == 0:
1569 return
1570
1571 if len(rows) > 1:
1572 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True)
1573 return
1574
1575 subst = self.get_selected_data()[0]
1576 edit_intake_of_substance(parent = self, substance = subst)
1577 #------------------------------------------------------------
1579
1580 rows = self.get_selected_rows()
1581
1582 if len(rows) == 0:
1583 return
1584
1585 if len(rows) > 1:
1586 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True)
1587 return
1588
1589 subst = self.get_selected_data()[0]
1590 delete_substance_intake(parent = self, substance = subst['pk_substance_intake'])
1591 #------------------------------------------------------------
1593 rows = self.get_selected_rows()
1594
1595 if len(rows) == 0:
1596 return
1597
1598 if len(rows) > 1:
1599 gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True)
1600 return
1601
1602 subst = self.get_selected_data()[0]
1603 if subst['is_currently_active']:
1604 subst['discontinued'] = gmDateTime.pydt_now_here()
1605 if subst['discontinue_reason'] is None:
1606 subst['discontinue_reason'] = _('discontinued due to allergy or intolerance')
1607 subst.save()
1608
1609 emr = self.__patient.get_emr()
1610 allg = subst.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
1611 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1)
1612 dlg.ShowModal()
1613 #------------------------------------------------------------
1615 # there could be some filtering/user interaction going on here
1616 _cfg = gmCfg2.gmCfgData()
1617 print_medication_list(parent = self, cleanup = _cfg.get(option = 'debug'))
1618 #------------------------------------------------------------
1620
1621 try:
1622 entry = self.__row_data[row]
1623 except KeyError:
1624 return u' '
1625
1626 emr = self.__patient.get_emr()
1627 atcs = []
1628 if entry['atc_substance'] is not None:
1629 atcs.append(entry['atc_substance'])
1630 # if entry['atc_brand'] is not None:
1631 # atcs.append(entry['atc_brand'])
1632 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), brand = entry['brand'])
1633 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],))
1634
1635 tt = _('Substance intake entry (%s, %s) [#%s] \n') % (
1636 gmTools.bool2subst (
1637 boolean = entry['is_currently_active'],
1638 true_return = gmTools.bool2subst (
1639 boolean = entry['seems_inactive'],
1640 true_return = _('active, needs check'),
1641 false_return = _('active'),
1642 none_return = _('assumed active')
1643 ),
1644 false_return = _('inactive')
1645 ),
1646 gmTools.bool2subst (
1647 boolean = entry['intake_is_approved_of'],
1648 true_return = _('approved'),
1649 false_return = _('unapproved')
1650 ),
1651 entry['pk_substance_intake']
1652 )
1653
1654 if allg not in [None, False]:
1655 certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected'))
1656 tt += u'\n'
1657 tt += u' !! ---- Cave ---- !!\n'
1658 tt += u' %s (%s): %s (%s)\n' % (
1659 allg['l10n_type'],
1660 certainty,
1661 allg['descriptor'],
1662 gmTools.coalesce(allg['reaction'], u'')[:40]
1663 )
1664 tt += u'\n'
1665
1666 tt += u' ' + _('Substance: %s [#%s]\n') % (entry['substance'], entry['pk_substance'])
1667 tt += u' ' + _('Preparation: %s\n') % entry['preparation']
1668 if entry['strength'] is not None:
1669 tt += u' ' + _('Amount per dose: %s') % entry['strength']
1670 if entry.ddd is not None:
1671 tt += u' (DDD: %s %s)' % (entry.ddd['ddd'], entry.ddd['unit'])
1672 tt += u'\n'
1673 else:
1674 if entry.ddd is not None:
1675 tt += u' DDD: %s %s' % (entry.ddd['ddd'], entry.ddd['unit'])
1676 tt += u'\n'
1677 tt += gmTools.coalesce(entry['atc_substance'], u'', _(' ATC (substance): %s\n'))
1678
1679 tt += u'\n'
1680
1681 tt += gmTools.coalesce (
1682 entry['brand'],
1683 u'',
1684 _(' Brand name: %%s [#%s]\n') % entry['pk_brand']
1685 )
1686 tt += gmTools.coalesce(entry['atc_brand'], u'', _(' ATC (brand): %s\n'))
1687
1688 tt += u'\n'
1689
1690 tt += gmTools.coalesce(entry['schedule'], u'', _(' Regimen: %s\n'))
1691
1692 if entry['is_long_term']:
1693 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity)
1694 else:
1695 if entry['duration'] is None:
1696 duration = u''
1697 else:
1698 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days))
1699
1700 tt += _(' Started %s%s%s\n') % (
1701 entry['started'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
1702 duration,
1703 gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), u'')
1704 )
1705
1706 if entry['discontinued'] is not None:
1707 tt += _(' Discontinued %s\n') % (
1708 entry['discontinued'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
1709 )
1710 tt += _(' Reason: %s\n') % entry['discontinue_reason']
1711
1712 tt += u'\n'
1713
1714 tt += gmTools.coalesce(entry['aim'], u'', _(' Aim: %s\n'))
1715 tt += gmTools.coalesce(entry['episode'], u'', _(' Episode: %s\n'))
1716 tt += gmTools.coalesce(entry['notes'], u'', _(' Advice: %s\n'))
1717
1718 tt += u'\n'
1719
1720 tt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({
1721 'row_ver': entry['row_version'],
1722 'mod_when': entry['modified_when'].strftime('%c').decode(gmI18N.get_encoding()),
1723 'mod_by': entry['modified_by']
1724 })
1725
1726 return tt
1727 #------------------------------------------------------------
1728 # internal helpers
1729 #------------------------------------------------------------
1731 self.CreateGrid(0, 1)
1732 self.EnableEditing(0)
1733 self.EnableDragGridSize(1)
1734 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
1735
1736 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
1737
1738 self.SetRowLabelSize(0)
1739 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1740 #------------------------------------------------------------
1741 # properties
1742 #------------------------------------------------------------
1745
1749
1750 patient = property(_get_patient, _set_patient)
1751 #------------------------------------------------------------
1754
1758
1759 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
1760 #------------------------------------------------------------
1763
1767
1768 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
1769 #------------------------------------------------------------
1772
1776
1777 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
1778 #------------------------------------------------------------
1779 # event handling
1780 #------------------------------------------------------------
1782 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow
1783 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1784 #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1785 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels)
1786
1787 # editing cells
1788 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1789 #------------------------------------------------------------
1791 """Calculate where the mouse is and set the tooltip dynamically."""
1792
1793 # Use CalcUnscrolledPosition() to get the mouse position within the
1794 # entire grid including what's offscreen
1795 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1796
1797 # use this logic to prevent tooltips outside the actual cells
1798 # apply to GetRowSize, too
1799 # tot = 0
1800 # for col in xrange(self.NumberCols):
1801 # tot += self.GetColSize(col)
1802 # if xpos <= tot:
1803 # self.tool_tip.Tip = 'Tool tip for Column %s' % (
1804 # self.GetColLabelValue(col))
1805 # break
1806 # else: # mouse is in label area beyond the right-most column
1807 # self.tool_tip.Tip = ''
1808
1809 row, col = self.XYToCell(x, y)
1810
1811 if row == self.__prev_tooltip_row:
1812 return
1813
1814 self.__prev_tooltip_row = row
1815
1816 try:
1817 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1818 except KeyError:
1819 pass
1820 #------------------------------------------------------------
1822 row = evt.GetRow()
1823 data = self.__row_data[row]
1824 edit_intake_of_substance(parent = self, substance = data)
1825 #============================================================
1826 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
1827
1828 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
1829
1830 """Panel holding a grid with current substances. Used as notebook page."""
1831
1833
1834 wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs)
1835 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1836
1837 self.__register_interests()
1838 #-----------------------------------------------------
1839 # reget-on-paint mixin API
1840 #-----------------------------------------------------
1842 """Populate cells with data from model."""
1843 pat = gmPerson.gmCurrentPatient()
1844 if pat.connected:
1845 self._grid_substances.patient = pat
1846 else:
1847 self._grid_substances.patient = None
1848 return True
1849 #--------------------------------------------------------
1850 # event handling
1851 #--------------------------------------------------------
1853 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1854 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
1855 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
1856 # active_substance_mod_db
1857 # substance_brand_mod_db
1858 #--------------------------------------------------------
1861 #--------------------------------------------------------
1863 self._grid_substances.patient = None
1864 #--------------------------------------------------------
1867 #--------------------------------------------------------
1870 #--------------------------------------------------------
1873 #--------------------------------------------------------
1876 #--------------------------------------------------------
1879 #--------------------------------------------------------
1881 self._grid_substances.grouping_mode = 'episode'
1882 #--------------------------------------------------------
1884 self._grid_substances.grouping_mode = 'brand'
1885 #--------------------------------------------------------
1888 #--------------------------------------------------------
1891 #--------------------------------------------------------
1894 #--------------------------------------------------------
1897 #--------------------------------------------------------
1900 #--------------------------------------------------------
1903 #============================================================
1904 # main
1905 #------------------------------------------------------------
1906 if __name__ == '__main__':
1907
1908 if len(sys.argv) < 2:
1909 sys.exit()
1910
1911 if sys.argv[1] != 'test':
1912 sys.exit()
1913
1914 from Gnumed.pycommon import gmI18N
1915
1916 gmI18N.activate_locale()
1917 gmI18N.install_domain(domain = 'gnumed')
1918
1919 #----------------------------------------
1920 app = wx.PyWidgetTester(size = (600, 600))
1921 app.SetWidget(cATCPhraseWheel, -1)
1922 app.MainLoop()
1923
1924 #============================================================
1925
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Nov 29 04:04:42 2010 | http://epydoc.sourceforge.net |