1
2 """GNUmed Praxis related middleware."""
3
4 __license__ = "GPL"
5 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
6
7
8 import sys
9 import logging
10 import io
11 import urllib.parse
12
13
14 if __name__ == '__main__':
15 sys.path.insert(0, '../../')
16 from Gnumed.pycommon import gmPG2
17 from Gnumed.pycommon import gmTools
18 from Gnumed.pycommon import gmBorg
19 from Gnumed.pycommon import gmCfg2
20 from Gnumed.pycommon import gmHooks
21 from Gnumed.pycommon import gmBusinessDBObject
22
23 from Gnumed.business import gmOrganization
24
25
26 _log = logging.getLogger('gm.praxis')
27 _cfg = gmCfg2.gmCfgData()
28
29
31
32 args = {'wp': workplace}
33
34
35 queries = [
36 {'cmd': """
37 delete from cfg.cfg_item
38 where
39 fk_template = (
40 select pk
41 from cfg.cfg_template
42 where name = 'horstspace.notebook.plugin_load_order'
43 )
44 and
45 workplace = %(wp)s""",
46 'args': args
47 }
48 ]
49
50
51 if delete_config:
52 queries.append ({
53 'cmd': """
54 delete from cfg.cfg_item
55 where
56 workplace = %(wp)s""",
57 'args': args
58 })
59
60 gmPG2.run_rw_queries(link_obj = conn, queries = queries, end_tx = True)
61
62
63
64
65 _SQL_get_praxis_branches = "SELECT * FROM dem.v_praxis_branches WHERE %s"
66
68 """Represents a praxis branch"""
69
70 _cmd_fetch_payload = _SQL_get_praxis_branches % "pk_praxis_branch = %s"
71 _cmds_store_payload = [
72 """UPDATE dem.praxis_branch SET
73 fk_org_unit = %(pk_org_unit)s
74 WHERE
75 pk = %(pk_praxis_branch)s
76 AND
77 xmin = %(xmin_praxis_branch)s
78 RETURNING
79 xmin as xmin_praxis_branch
80 """
81 ]
82 _updatable_fields = [
83 'pk_org_unit'
84 ]
85
91
92
93 - def lock(self, exclusive=False):
94 return lock_praxis_branch(pk_praxis_branch = self._payload[self._idx['pk_praxis_branch']], exclusive = exclusive)
95
96
97 - def unlock(self, exclusive=False):
98 return unlock_praxis_branch(pk_praxis_branch = self._payload[self._idx['pk_praxis_branch']], exclusive = exclusive)
99
100
103
104
107
108
110 self_adr = self.address
111 url = 'https://www.luftlinie.org/%s-%s-%s-%s-%s/%s-%s-%s-%s-%s' % (
112 urllib.parse.quote(self_adr['street'].encode('utf8')),
113 urllib.parse.quote(self_adr['number'].encode('utf8')),
114 urllib.parse.quote(self_adr['urb'].encode('utf8')),
115 urllib.parse.quote(self_adr['postcode'].encode('utf8')),
116 urllib.parse.quote(self_adr['country'].encode('utf8')),
117 urllib.parse.quote(address['street'].encode('utf8')),
118 urllib.parse.quote(address['number'].encode('utf8')),
119 urllib.parse.quote(address['urb'].encode('utf8')),
120 urllib.parse.quote(address['postcode'].encode('utf8')),
121 urllib.parse.quote(address['country'].encode('utf8'))
122 )
123 return url
124
125
126
127
130
131 org_unit = property(_get_org_unit, lambda x:x)
132
133
136
137 organization = property(_get_org, lambda x:x)
138
139
142
143 address = property(_get_address, lambda x:x)
144
145
146
147
148
149
150
152 vcf_fields = [
153 'BEGIN:VCARD',
154 'VERSION:4.0',
155 'KIND:org',
156 'ORG:%(l10n_organization_category)s %(praxis)s;%(l10n_unit_category)s %(branch)s' % self,
157 _('FN:%(l10n_unit_category)s %(branch)s of %(l10n_organization_category)s %(praxis)s') % self,
158 'N:%(praxis)s;%(branch)s' % self
159 ]
160 adr = self.address
161 if adr is not None:
162 vcf_fields.append('ADR:;%(subunit)s;%(street)s %(number)s;%(urb)s;%(l10n_region)s;%(postcode)s;%(l10n_country)s' % adr)
163 comms = self.get_comm_channels(comm_medium = 'workphone')
164 if len(comms) > 0:
165 vcf_fields.append('TEL;VALUE=uri;TYPE=work:tel:%(url)s' % comms[0])
166 comms = self.get_comm_channels(comm_medium = 'email')
167 if len(comms) > 0:
168 vcf_fields.append('EMAIL:%(url)s' % comms[0])
169 vcf_fields.append('END:VCARD')
170 vcf_fname = gmTools.get_unique_filename (
171 prefix = 'gm-praxis-',
172 suffix = '.vcf'
173 )
174 vcf_file = io.open(vcf_fname, mode = 'wt', encoding = 'utf8')
175 vcf_file.write('\n'.join(vcf_fields))
176 vcf_file.write('\n')
177 vcf_file.close()
178 return vcf_fname
179
180 vcf = property(_get_vcf, lambda x:x)
181
182
192
193
195 """
196 http://blog.thenetimpact.com/2011/07/decoding-qr-codes-how-to-format-data-for-qr-code-generators/
197 https://www.nttdocomo.co.jp/english/service/developer/make/content/barcode/function/application/addressbook/index.html
198
199 MECARD:N:NAME;ADR:pobox,subunit,unit,street,ort,region,zip,country;TEL:111111111;FAX:22222222;EMAIL:mail@praxis.org;
200
201 MECARD:N:$<praxis::%(praxis)s, %(branch)s::>$;ADR:$<praxis_address::,%(subunit)s,%(number)s,%(street)s,%(urb)s,,%(postcode)s,%(l10n_country)s::>$;TEL:$<praxis_comm::workphone::>$;FAX:$<praxis_comm::fax::>$;EMAIL:$<praxis_comm::email::60>$;
202 """
203 MECARD = 'MECARD:N:%(praxis)s,%(branch)s;' % self
204 adr = self.address
205 if adr is not None:
206 MECARD += 'ADR:,%(subunit)s,%(number)s,%(street)s,%(urb)s,,%(postcode)s,%(l10n_country)s;' % adr
207 comms = self.get_comm_channels(comm_medium = 'workphone')
208 if len(comms) > 0:
209 MECARD += 'TEL:%(url)s;' % comms[0]
210 comms = self.get_comm_channels(comm_medium = 'fax')
211 if len(comms) > 0:
212 MECARD += 'FAX:%(url)s;' % comms[0]
213 comms = self.get_comm_channels(comm_medium = 'email')
214 if len(comms) > 0:
215 MECARD += 'EMAIL:%(url)s;' % comms[0]
216 return MECARD
217
218 MECARD = property(_get_mecard)
219
220
222 IBANs = self.get_external_ids(id_type = 'IBAN', issuer = 'Bank')
223 if len(IBANs) == 0:
224 _log.debug('no IBAN found, cannot create scan2pay data')
225 return None
226 data = {
227 'IBAN': IBANs[0]['value'][:34],
228 'beneficiary': self['praxis'][:70]
229 }
230 BICs = self.get_external_ids(id_type = 'BIC', issuer = 'Bank')
231 if len(BICs) == 0:
232 data['BIC'] = ''
233 else:
234 data['BIC'] = BICs[0]['value'][:11]
235 return 'BCD\n002\n1\nSCT\n%(BIC)s\n%(beneficiary)s\n%(IBAN)s' % data
236
237 scan2pay_data = property(_get_scan2pay_data)
238
239
241 return gmPG2.lock_row(table = 'dem.praxis_branch', pk = pk_praxis_branch, exclusive = exclusive)
242
243
245 return gmPG2.unlock_row(table = 'dem.praxis_branch', pk = pk_praxis_branch, exclusive = exclusive)
246
247
249 if order_by is None:
250 order_by = 'true'
251 else:
252 order_by = 'true ORDER BY %s' % order_by
253
254 cmd = _SQL_get_praxis_branches % order_by
255 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
256 if return_pks:
257 return [ r['pk_praxis_branch'] for r in rows ]
258 return [ cPraxisBranch(row = {'data': r, 'idx': idx, 'pk_field': 'pk_praxis_branch'}) for r in rows ]
259
260
262 cmd = _SQL_get_praxis_branches % 'pk_org_unit = %(pk_ou)s'
263 args = {'pk_ou': pk_org_unit}
264 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
265 if len(rows) == 0:
266 return None
267 return cPraxisBranch(row = {'data': rows[0], 'idx': idx, 'pk_field': 'pk_praxis_branch'})
268
269
271
272 args = {'fk_unit': pk_org_unit}
273 cmd1 = """
274 INSERT INTO dem.praxis_branch (fk_org_unit)
275 SELECT %(fk_unit)s WHERE NOT EXISTS (
276 SELECT 1 FROM dem.praxis_branch WHERE fk_org_unit = %(fk_unit)s
277 )
278 """
279 cmd2 = """SELECT * from dem.v_praxis_branches WHERE pk_org_unit = %(fk_unit)s"""
280 queries = [
281 {'cmd': cmd1, 'args': args},
282 {'cmd': cmd2, 'args': args}
283 ]
284 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True, get_col_idx = True)
285 return cPraxisBranch(row = {'data': rows[0], 'idx': idx, 'pk_field': 'pk_praxis_branch'})
286
287
289 queries = []
290 for pk in pk_org_units:
291 args = {'fk_unit': pk}
292 cmd = """
293 INSERT INTO dem.praxis_branch (fk_org_unit)
294 SELECT %(fk_unit)s WHERE NOT EXISTS (
295 SELECT 1 FROM dem.praxis_branch WHERE fk_org_unit = %(fk_unit)s
296 )
297 """
298 queries.append({'cmd': cmd, 'args': args})
299
300 args = {'fk_units': tuple(pk_org_units)}
301 cmd = """SELECT * from dem.v_praxis_branches WHERE pk_org_unit IN %(fk_units)s"""
302 queries.append({'cmd': cmd, 'args': args})
303 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True, get_col_idx = True)
304 return [ cPraxisBranch(row = {'data': r, 'idx': idx, 'pk_field': 'pk_praxis_branch'}) for r in rows ]
305
306
308 if not lock_praxis_branch(pk_praxis_branch = pk_praxis_branch, exclusive = True):
309 return False
310 args = {'pk': pk_praxis_branch}
311 cmd = "DELETE FROM dem.praxis_branch WHERE pk = %(pk)s"
312 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
313 unlock_praxis_branch(pk_praxis_branch = pk_praxis_branch, exclusive = True)
314 return True
315
316
318
319 if pk_praxis_branches is None:
320 cmd = 'SELECT pk from dem.praxis_branch'
321 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
322 pks_to_lock = [ r[0] for r in rows ]
323 else:
324 pks_to_lock = pk_praxis_branches[:]
325
326 if except_pk_praxis_branches is not None:
327 for pk in except_pk_praxis_branches:
328 try: pks_to_lock.remove(pk)
329 except ValueError: pass
330
331 for pk in pks_to_lock:
332 if not lock_praxis_branch(pk_praxis_branch = pk, exclusive = True):
333 return False
334
335 args = {}
336 where_parts = []
337
338 if pk_praxis_branches is not None:
339 args['pks'] = tuple(pk_praxis_branches)
340 where_parts.append('pk IN %(pks)s')
341
342 if except_pk_praxis_branches is not None:
343 args['except'] = tuple(except_pk_praxis_branches)
344 where_parts.append('pk NOT IN %(except)s')
345
346 if len(where_parts) == 0:
347 cmd = "DELETE FROM dem.praxis_branch"
348 else:
349 cmd = "DELETE FROM dem.praxis_branch WHERE %s" % ' AND '.join(where_parts)
350
351 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
352 for pk in pks_to_lock:
353 unlock_praxis_branch(pk_praxis_branch = pk, exclusive = True)
354 return True
355
356
358
360 try:
361 self.has_been_initialized
362 except AttributeError:
363 self.branch = None
364 self.has_been_initialized = True
365 self.__helpdesk = None
366 self.__active_workplace = None
367
368
369 if branch is None:
370 return None
371
372
373 if not isinstance(branch, cPraxisBranch):
374 _log.error('cannot set current praxis branch to [%s], must be a cPraxisBranch instance' % str(branch))
375 raise TypeError('gmPraxis.gmCurrentPraxisBranch.__init__(): <branch> must be a cPraxisBranch instance but is: %s' % str(branch))
376
377 if self.branch is not None:
378 self.branch.unlock()
379
380 branch.lock()
381 self.branch = branch
382 _log.debug('current praxis branch now: %s', self.branch)
383
384 return None
385
386
387
388
390 if attribute == 'has_been_initialized':
391 raise AttributeError
392 if attribute in ['branch', 'waiting_list_patients', 'help_desk', 'db_logon_banner', 'active_workplace', 'workplaces', 'user_email']:
393 return getattr(self, attribute)
394 return getattr(self.branch, attribute)
395
396
397
398
400 """Return any attribute if known how to retrieve it by proxy."""
401 return self.branch[attribute]
402
403
405 self.branch[attribute] = value
406
407
408
409
415
416
418 cmd = """
419 update clin.waiting_list
420 set
421 urgency = %(urg)s,
422 comment = %(cmt)s,
423 area = %(zone)s
424 where
425 pk = %(pk)s"""
426 args = {
427 'pk': pk,
428 'urg': urgency,
429 'cmt': gmTools.none_if(comment, ''),
430 'zone': gmTools.none_if(zone, '')
431 }
432
433 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
434 gmHooks.run_hook_script(hook = 'after_waiting_list_modified')
435
436
438 if current_position == 1:
439 return
440
441 cmd = 'select clin.move_waiting_list_entry(%(pos)s, (%(pos)s - 1))'
442 args = {'pos': current_position}
443
444 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
445
446
448 cmd = 'select clin.move_waiting_list_entry(%(pos)s, (%(pos)s+1))'
449 args = {'pos': current_position}
450
451 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
452
453
454
455
457 cmd = """
458 SELECT * FROM clin.v_waiting_list
459 ORDER BY
460 list_position
461 """
462 rows, idx = gmPG2.run_ro_queries (
463 queries = [{'cmd': cmd}],
464 get_col_idx = False
465 )
466 return rows
467
468 waiting_list_patients = property (_get_waiting_list_patients, lambda x:x)
469
470
473
475
476 if self.__helpdesk is not None:
477 return self.__helpdesk
478
479 self.__helpdesk = gmTools.coalesce (
480 _cfg.get (
481 group = 'workplace',
482 option = 'help desk',
483 source_order = [
484 ('explicit', 'return'),
485 ('workbase', 'return'),
486 ('local', 'return'),
487 ('user', 'return'),
488 ('system', 'return')
489 ]
490 ),
491 'http://wiki.gnumed.de'
492 )
493
494 return self.__helpdesk
495
496 helpdesk = property(_get_helpdesk, _set_helpdesk)
497
498
500 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': 'select _(message) from cfg.db_logon_banner'}])
501 if len(rows) == 0:
502 return ''
503 return gmTools.coalesce(rows[0][0], '').strip()
504
506 queries = [
507 {'cmd': 'delete from cfg.db_logon_banner'}
508 ]
509 if banner.strip() != '':
510 queries.append ({
511 'cmd': 'insert into cfg.db_logon_banner (message) values (%(msg)s)',
512 'args': {'msg': banner.strip()}
513 })
514 rows, idx = gmPG2.run_rw_queries(queries = queries, end_tx = True)
515
516 db_logon_banner = property(_get_db_logon_banner, _set_db_logon_banner)
517
518
522
524 """Return the current workplace (client profile) definition.
525
526 The first occurrence counts.
527 """
528 if self.__active_workplace is not None:
529 return self.__active_workplace
530
531 self.__active_workplace = gmTools.coalesce (
532 _cfg.get (
533 group = 'workplace',
534 option = 'name',
535 source_order = [
536 ('explicit', 'return'),
537 ('workbase', 'return'),
538 ('local', 'return'),
539 ('user', 'return'),
540 ('system', 'return'),
541 ]
542 ),
543 'Local Default'
544 )
545
546 return self.__active_workplace
547
548 active_workplace = property(_get_workplace, _set_workplace)
549
550
553
555 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': 'SELECT DISTINCT workplace FROM cfg.cfg_item ORDER BY workplace'}])
556 return [ r[0] for r in rows ]
557
558 workplaces = property(_get_workplaces, _set_workplaces)
559
560
562
563 return _cfg.get (
564 group = 'preferences',
565 option = 'user email',
566 source_order = [
567 ('explicit', 'return'),
568 ('user', 'return'),
569 ('local', 'return'),
570 ('workbase', 'return'),
571 ('system', 'return')
572 ]
573 )
574
584
585 user_email = property(_get_user_email, _set_user_email)
586
587
595
596
597 if __name__ == '__main__':
598
599 if len(sys.argv) < 2:
600 sys.exit()
601
602 if sys.argv[1] != 'test':
603 sys.exit()
604
605 from Gnumed.pycommon import gmI18N
606 gmI18N.install_domain()
607
626
627
628
629
630
631 for b in get_praxis_branches():
632 print((b.format()))
633
634 print(b.scan2pay_data)
635
636
637