1 """GNUmed praxis related widgets.
2
3 Praxis:
4
5 Each database belongs to ONE praxis only. A praxis can
6 have several branches. A praxis is at the same level
7 as an organization, except that it is not explicitely
8 defined. Rather, that ONE organization of which at least
9 one unit is defined as a praxis branch IS the praxis.
10
11 Praxis branch
12
13 Branches are the sites/locations of a praxis. There
14 can be several branches. Each branch must link to
15 units of ONE AND THE SAME organization (because
16 it is not considered good data protection practice
17 to mix charts of *different* organizations within
18 one database). However, that organization which
19 has units that are praxis branches can also have
20 other units which are not branches :-)
21
22 copyright: authors
23 """
24
25 __author__ = "K.Hilbert"
26 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
27
28 import logging
29 import sys
30
31
32
33 import wx
34
35
36 if __name__ == '__main__':
37 sys.path.insert(0, '../../')
38 from Gnumed.pycommon import gmCfg
39 from Gnumed.pycommon import gmDispatcher
40 from Gnumed.pycommon import gmTools
41 from Gnumed.pycommon import gmPG2
42 from Gnumed.pycommon import gmMatchProvider
43
44 from Gnumed.business import gmPraxis
45 from Gnumed.business import gmStaff
46 from Gnumed.business import gmOrganization
47
48 from Gnumed.wxpython import gmOrganizationWidgets
49 from Gnumed.wxpython import gmGuiHelpers
50 from Gnumed.wxpython import gmAuthWidgets
51 from Gnumed.wxpython import gmListWidgets
52 from Gnumed.wxpython import gmPlugin
53 from Gnumed.wxpython import gmCfgWidgets
54 from Gnumed.wxpython import gmPhraseWheel
55
56
57 _log = logging.getLogger('gm.praxis')
58
59
61
62 if parent is None:
63 parent = wx.GetApp().GetTopWindow()
64
65 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('showing audit trail'))
66 if conn is None:
67 return False
68
69
70 def refresh(lctrl):
71 cmd = 'SELECT * FROM audit.v_audit_trail ORDER BY audit_when_ts'
72 rows, idx = gmPG2.run_ro_queries(link_obj = conn, queries = [{'cmd': cmd}], get_col_idx = False)
73 lctrl.set_string_items (
74 [ [
75 r['event_when'],
76 r['event_by'],
77 '%s %s %s' % (
78 gmTools.coalesce(r['row_version_before'], gmTools.u_diameter),
79 gmTools.u_arrow2right,
80 gmTools.coalesce(r['row_version_after'], gmTools.u_diameter)
81 ),
82 r['event_table'],
83 r['event'],
84 r['pk_audit']
85 ] for r in rows ]
86 )
87
88 gmListWidgets.get_choices_from_list (
89 parent = parent,
90 msg = '',
91 caption = _('GNUmed database audit log ...'),
92 columns = [ _('When'), _('Who'), _('Revisions'), _('Table'), _('Event'), '#' ],
93 single_selection = True,
94 refresh_callback = refresh
95 )
96
97
134
135
136
137
182
183 def edit(workplace=None):
184
185 dbcfg = gmCfg.cCfgSQL()
186
187 if workplace is None:
188 dlg = wx.TextEntryDialog (
189 parent,
190 _('Enter a descriptive name for the new workplace:'),
191 caption = _('Configuring GNUmed workplaces ...'),
192 value = '',
193 style = wx.OK | wx.CENTRE
194 )
195 dlg.ShowModal()
196 workplace = dlg.GetValue().strip()
197 if workplace == '':
198 gmGuiHelpers.gm_show_error(_('Cannot save a new workplace without a name.'), _('Configuring GNUmed workplaces ...'))
199 return False
200 curr_plugins = []
201 else:
202 curr_plugins = gmTools.coalesce(dbcfg.get2 (
203 option = 'horstspace.notebook.plugin_load_order',
204 workplace = workplace,
205 bias = 'workplace'
206 ), []
207 )
208
209 msg = _(
210 'Pick the plugin(s) to be loaded the next time the client is restarted under the workplace:\n'
211 '\n'
212 ' [%s]\n'
213 ) % workplace
214
215 picker = gmListWidgets.cItemPickerDlg (
216 parent,
217 -1,
218 title = _('Configuring workplace plugins ...'),
219 msg = msg
220 )
221 picker.set_columns(['Available plugins'], ['Active plugins'])
222 available_plugins = gmPlugin.get_installed_plugins(plugin_dir = 'gui')
223 picker.set_choices(available_plugins)
224 picker.set_picks(picks = curr_plugins[:])
225 btn_pressed = picker.ShowModal()
226 if btn_pressed != wx.ID_OK:
227 picker.DestroyLater()
228 return False
229
230 new_plugins = picker.get_picks()
231 picker.DestroyLater()
232 if new_plugins == curr_plugins:
233 return True
234
235 if new_plugins is None:
236 return True
237
238 dbcfg.set (
239 option = 'horstspace.notebook.plugin_load_order',
240 value = new_plugins,
241 workplace = workplace
242 )
243
244 return True
245
246 def edit_old(workplace=None):
247
248 available_plugins = gmPlugin.get_installed_plugins(plugin_dir='gui')
249
250 dbcfg = gmCfg.cCfgSQL()
251
252 if workplace is None:
253 dlg = wx.TextEntryDialog (
254 parent,
255 _('Enter a descriptive name for the new workplace:'),
256 caption = _('Configuring GNUmed workplaces ...'),
257 value = '',
258 style = wx.OK | wx.CENTRE
259 )
260 dlg.ShowModal()
261 workplace = dlg.GetValue().strip()
262 if workplace == '':
263 gmGuiHelpers.gm_show_error(_('Cannot save a new workplace without a name.'), _('Configuring GNUmed workplaces ...'))
264 return False
265 curr_plugins = []
266 choices = available_plugins
267 else:
268 curr_plugins = gmTools.coalesce(dbcfg.get2 (
269 option = 'horstspace.notebook.plugin_load_order',
270 workplace = workplace,
271 bias = 'workplace'
272 ), []
273 )
274 choices = curr_plugins[:]
275 for p in available_plugins:
276 if p not in choices:
277 choices.append(p)
278
279 sels = range(len(curr_plugins))
280 new_plugins = gmListWidgets.get_choices_from_list (
281 parent = parent,
282 msg = _(
283 '\n'
284 'Select the plugin(s) to be loaded the next time\n'
285 'the client is restarted under the workplace:\n'
286 '\n'
287 ' [%s]'
288 '\n'
289 ) % workplace,
290 caption = _('Configuring GNUmed workplaces ...'),
291 choices = choices,
292 selections = sels,
293 columns = [_('Plugins')],
294 single_selection = False
295 )
296
297 if new_plugins == curr_plugins:
298 return True
299
300 if new_plugins is None:
301 return True
302
303 dbcfg.set (
304 option = 'horstspace.notebook.plugin_load_order',
305 value = new_plugins,
306 workplace = workplace
307 )
308
309 return True
310
311 def clone(workplace=None):
312 if workplace is None:
313 return False
314
315 new_name = wx.GetTextFromUser (
316 message = _('Enter a name for the new workplace !'),
317 caption = _('Cloning workplace'),
318 default_value = '%s-2' % workplace,
319 parent = parent
320 ).strip()
321
322 if new_name == '':
323 return False
324
325 dbcfg = gmCfg.cCfgSQL()
326 opt = 'horstspace.notebook.plugin_load_order'
327
328 plugins = dbcfg.get2 (
329 option = opt,
330 workplace = workplace,
331 bias = 'workplace'
332 )
333
334 dbcfg.set (
335 option = opt,
336 value = plugins,
337 workplace = new_name
338 )
339
340
341
342 return True
343
344 def refresh(lctrl):
345 workplaces = gmPraxis.gmCurrentPraxisBranch().workplaces
346 curr_workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace
347 try:
348 sels = [workplaces.index(curr_workplace)]
349 except ValueError:
350 sels = []
351
352 lctrl.set_string_items(workplaces)
353 lctrl.set_selections(selections = sels)
354
355 gmListWidgets.get_choices_from_list (
356 parent = parent,
357 msg = _(
358 '\nSelect the workplace to configure below.\n'
359 '\n'
360 'The currently active workplace is preselected.\n'
361 ),
362 caption = _('Configuring GNUmed workplaces ...'),
363 columns = [_('Workplace')],
364 single_selection = True,
365 refresh_callback = refresh,
366 edit_callback = edit,
367 new_callback = edit,
368 delete_callback = delete,
369 left_extra_button = (_('Clone'), _('Clone the selected workplace'), clone)
370 )
371
372
373 from Gnumed.wxGladeWidgets import wxgGreetingEditorDlg
374
391
392
400
401
402
403
405
406 if no_parent:
407 parent = None
408 else:
409 if parent is None:
410 parent = wx.GetApp().GetTopWindow()
411
412 branches = gmPraxis.get_praxis_branches()
413
414 if len(branches) == 1:
415 _log.debug('only one praxis branch configured')
416 gmPraxis.gmCurrentPraxisBranch(branches[0])
417 _log.debug('activated branch')
418 return True
419
420 if len(branches) == 0:
421 orgs = gmOrganization.get_orgs()
422 if len(orgs) == 0:
423 pk_cat = gmOrganization.create_org_category(category = 'Praxis')
424 org = gmOrganization.create_org(_('Your praxis'), pk_cat)
425 unit = org.add_unit(_('Your branch'))
426 branch = gmPraxis.create_praxis_branch(pk_org_unit = unit['pk_org_unit'])
427 _log.debug('auto-created praxis branch because no organizations existed: %s', branch)
428 gmPraxis.gmCurrentPraxisBranch(branch)
429 gmGuiHelpers.gm_show_info (
430 title = _('Praxis configuration ...'),
431 info = _(
432 'GNUmed has auto-created the following\n'
433 'praxis branch for you (which you can\n'
434 'later configure as needed):\n'
435 '\n'
436 '%s'
437 ) % branch.format()
438 )
439 return True
440
441 if len(orgs) == 1:
442 units = orgs[0].units
443 if len(units) == 1:
444 branch = gmPraxis.create_praxis_branch(pk_org_unit = units[0]['pk_org_unit'])
445 _log.debug('auto-selected praxis branch because only one organization with only one unit existed: %s', branch)
446 gmPraxis.gmCurrentPraxisBranch(branch)
447 gmGuiHelpers.gm_show_info (
448 title = _('Praxis configuration ...'),
449 info = _(
450 'GNUmed has auto-selected the following\n'
451 'praxis branch for you (which you can\n'
452 'later configure as needed):\n'
453 '\n'
454 '%s'
455 ) % branch.format()
456 )
457 return True
458
459 _log.debug('no praxis branches configured, selecting from organization units')
460 msg = _(
461 'No praxis branches configured currently.\n'
462 '\n'
463 'You MUST select one unit of an organization to be the initial\n'
464 'branch (site, office) which you are logging in from.'
465 )
466 unit = gmOrganizationWidgets.select_org_unit(msg = msg, no_parent = True)
467 if unit is None:
468 _log.warning('no organization unit selected, aborting')
469 return False
470 _log.debug('org unit selected as praxis branch: %s', unit)
471 branch = gmPraxis.create_praxis_branch(pk_org_unit = unit['pk_org_unit'])
472 _log.debug('created praxis branch: %s', branch)
473 gmPraxis.gmCurrentPraxisBranch(branch)
474 return True
475
476
477 def refresh(lctrl):
478 branches = gmPraxis.get_praxis_branches()
479 items = [
480 [ b['branch'],
481 gmTools.coalesce(b['l10n_unit_category'], '')
482 ] for b in branches
483 ]
484 lctrl.set_string_items(items = items)
485 lctrl.set_data(data = branches)
486
487 branch = gmListWidgets.get_choices_from_list (
488 parent = parent,
489 msg = _("Select the branch of praxis [%s] which you are logging in from.\n") % branches[0]['praxis'],
490 caption = _('Praxis branch selection ...'),
491 columns = [_('Branch'), _('Branch type')],
492 can_return_empty = False,
493 single_selection = True,
494 refresh_callback = refresh
495 )
496 if branch is None:
497 _log.warning('no praxis branch selected, aborting')
498 return False
499 gmPraxis.gmCurrentPraxisBranch(branch)
500 return True
501
502
504
505 if parent is None:
506 parent = wx.GetApp().GetTopWindow()
507
508
509 def get_unit_tooltip(unit):
510 if unit is None:
511 return None
512 return '\n'.join(unit.format(with_address = True, with_org = True, with_comms = True))
513
514 def manage_orgs():
515 gmOrganizationWidgets.manage_orgs(parent = parent)
516
517
518 branches = gmPraxis.get_praxis_branches()
519 org = branches[0].organization
520 praxis = branches[0]['praxis']
521
522 msg = _(
523 'Pick those units of "%s" which are branches of your praxis !\n'
524 '\n'
525 'Note that no other client should be connected at this time.\n'
526 '\n'
527 'If you want to select another organization entirely\n'
528 'first remove all existing branches.\n'
529 ) % praxis
530
531 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg)
532 picker.extra_button = (
533 _('Manage units'),
534 _('Manage organizations and their units'),
535 manage_orgs
536 )
537 picker.left_item_tooltip_callback = get_unit_tooltip
538 picker.right_item_tooltip_callback = get_unit_tooltip
539 picker.set_columns(columns = [_('Units of "%s"') % praxis], columns_right = [_('Branches of your praxis')])
540 units = org.units
541 branch_unit_pks = [b['pk_org_unit'] for b in branches]
542 branch_units = []
543 for unit in units:
544 if unit['pk_org_unit'] in branch_unit_pks:
545 branch_units.append(unit)
546 items = [ '%s%s' % (u['unit'], gmTools.coalesce(u['l10n_unit_category'], '', ' (%s)')) for u in units ]
547 picker.set_choices(choices = items, data = units)
548 items = [ '%s%s' % (u['unit'], gmTools.coalesce(u['l10n_unit_category'], '', ' (%s)')) for u in branch_units ]
549 picker.set_picks(picks = items, data = branch_units)
550 del units
551 del branch_unit_pks
552 del branch_units
553 del items
554
555 result = picker.ShowModal()
556
557 if result == wx.ID_CANCEL:
558 picker.DestroyLater()
559 return None
560
561 picks = picker.picks
562 picker.DestroyLater()
563
564 failed_delete_msg = _(
565 'Cannot delete praxis branch(es).\n'
566 '\n'
567 'There are probably clients logged in\n'
568 'from other locations. You need to log out\n'
569 'all but this client before the praxis can\n'
570 'be reconfigured.'
571 )
572
573 if len(picks) == 0:
574 if not gmPraxis.delete_praxis_branches():
575 gmGuiHelpers.gm_show_error (
576 error = failed_delete_msg,
577 title = _('Configuring praxis ...')
578 )
579 return False
580 while not set_active_praxis_branch(parent = parent):
581 pass
582 return
583
584 pk_picked_units = [p['pk_org_unit'] for p in picks]
585 pk_branches_to_keep = [
586 b['pk_praxis_branch'] for b in gmPraxis.get_praxis_branches()
587 if b['pk_org_unit'] in pk_picked_units
588 ]
589 if len(pk_branches_to_keep) == 0:
590 if not gmPraxis.delete_praxis_branches():
591 gmGuiHelpers.gm_show_error (
592 error = failed_delete_msg,
593 title = _('Configuring praxis ...')
594 )
595 return False
596 else:
597 if not gmPraxis.delete_praxis_branches(except_pk_praxis_branches = pk_branches_to_keep):
598 gmGuiHelpers.gm_show_error (
599 error = failed_delete_msg,
600 title = _('Configuring praxis ...')
601 )
602 return False
603 gmPraxis.create_praxis_branches(pk_org_units = pk_picked_units)
604
605
606 if gmPraxis.gmCurrentPraxisBranch()['pk_praxis_branch'] in pk_branches_to_keep:
607 return
608
609 while not set_active_praxis_branch(parent = parent):
610 pass
611
612
614
616 query = """(
617 SELECT
618 pk_praxis_branch AS data,
619 branch || ' (' || praxis || ')' AS field_label,
620 branch || coalesce(' (' || l10n_unit_category || ')', '') || ' of ' || l10n_organization_category || ' "' || praxis || '"' AS list_label
621 FROM
622 dem.v_praxis_branches
623 WHERE
624 branch %(fragment_condition)s
625
626 ) UNION (
627
628 SELECT
629 pk_praxis_branch AS data,
630 branch || ' (' || praxis || ')' AS field_label,
631 branch || coalesce(' (' || l10n_unit_category || ')', '') || ' of ' || l10n_organization_category || ' "' || praxis || '"' AS list_label
632 FROM
633 dem.v_praxis_branches
634 WHERE
635 praxis %(fragment_condition)s
636
637 ) UNION (
638
639 SELECT
640 pk_praxis_branch AS data,
641 branch || ' (' || praxis || ')' AS field_label,
642 branch || coalesce(' (' || l10n_unit_category || ')', '') || ' of ' || l10n_organization_category || ' "' || praxis || '"' AS list_label
643 FROM
644 dem.v_praxis_branches
645 WHERE
646 l10n_unit_category %(fragment_condition)s
647
648 )
649 ORDER BY
650 list_label
651 LIMIT 50"""
652 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
653 mp.setThresholds(1, 2, 4)
654 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
655 self.SetToolTip(_("Select a praxis branch."))
656 self.matcher = mp
657 self.selection_only = True
658 self.picklist_delay = 300
659
667
672
673
674
675
676 if __name__ == "__main__":
677
678 if len(sys.argv) < 2:
679 sys.exit()
680
681 if sys.argv[1] != 'test':
682 sys.exit()
683
684
685
686
687
688
692
693
694
695
696