| Home | Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: latin-1 -*-
2 """GNUmed forms classes
3
4 Business layer for printing all manners of forms, letters, scripts etc.
5
6 license: GPL v2 or later
7 """
8 #============================================================
9 __version__ = "$Revision: 1.79 $"
10 __author__ ="Ian Haywood <ihaywood@gnu.org>, karsten.hilbert@gmx.net"
11
12
13 import os, sys, time, os.path, logging
14 import codecs
15 import re as regex
16 import shutil
17 import random, platform, subprocess
18 import socket # needed for OOo on Windows
19 #, libxml2, libxslt
20 import shlex
21
22
23 if __name__ == '__main__':
24 sys.path.insert(0, '../../')
25 from Gnumed.pycommon import gmTools
26 from Gnumed.pycommon import gmDispatcher
27 from Gnumed.pycommon import gmExceptions
28 from Gnumed.pycommon import gmMatchProvider
29 from Gnumed.pycommon import gmBorg
30 from Gnumed.pycommon import gmLog2
31 from Gnumed.pycommon import gmMimeLib
32 from Gnumed.pycommon import gmShellAPI
33 from Gnumed.pycommon import gmCfg
34 from Gnumed.pycommon import gmBusinessDBObject
35 from Gnumed.pycommon import gmPG2
36
37 from Gnumed.business import gmPerson
38 from Gnumed.business import gmPersonSearch
39 from Gnumed.business import gmSurgery
40
41
42 _log = logging.getLogger('gm.forms')
43 _log.info(__version__)
44
45 #============================================================
46 # this order is also used in choice boxes for the engine
47 form_engine_abbrevs = [u'O', u'L', u'I', u'G', u'P']
48
49 form_engine_names = {
50 u'O': 'OpenOffice',
51 u'L': 'LaTeX',
52 u'I': 'Image editor',
53 u'G': 'Gnuplot script',
54 u'P': 'PDF forms'
55 }
56
57 form_engine_template_wildcards = {
58 u'O': u'*.o?t',
59 u'L': u'*.tex',
60 u'G': u'*.gpl',
61 u'P': u'*.pdf'
62 }
63
64 # is filled in further below after each engine is defined
65 form_engines = {}
66
67 #============================================================
68 # match providers
69 #============================================================
71
73
74 query = u"""
75 SELECT
76 name_long AS data,
77 name_long AS list_label,
78 name_long AS field_label
79 FROM ref.v_paperwork_templates
80 WHERE name_long %(fragment_condition)s
81 ORDER BY list_label
82 """
83 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
84 #============================================================
86
88
89 query = u"""
90 SELECT
91 name_short AS data,
92 name_short AS list_label,
93 name_short AS field_label
94 FROM ref.v_paperwork_templates
95 WHERE name_short %(fragment_condition)s
96 ORDER BY name_short
97 """
98 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
99 #============================================================
101
103
104 query = u"""
105 SELECT DISTINCT ON (list_label)
106 pk AS data,
107 _(name) || ' (' || name || ')' AS list_label,
108 _(name) AS field_label
109 FROM ref.form_types
110 WHERE
111 _(name) %(fragment_condition)s
112 OR
113 name %(fragment_condition)s
114 ORDER BY list_label
115 """
116 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
117 #============================================================
119
120 _cmd_fetch_payload = u'select * from ref.v_paperwork_templates where pk_paperwork_template = %s'
121
122 _cmds_store_payload = [
123 u"""update ref.paperwork_templates set
124 name_short = %(name_short)s,
125 name_long = %(name_long)s,
126 fk_template_type = %(pk_template_type)s,
127 instance_type = %(instance_type)s,
128 engine = %(engine)s,
129 in_use = %(in_use)s,
130 filename = %(filename)s,
131 external_version = %(external_version)s
132 where
133 pk = %(pk_paperwork_template)s and
134 xmin = %(xmin_paperwork_template)s
135 """,
136 u"""select xmin_paperwork_template from ref.v_paperwork_templates where pk_paperwork_template = %(pk_paperwork_template)s"""
137 ]
138
139 _updatable_fields = [
140 u'name_short',
141 u'name_long',
142 u'external_version',
143 u'pk_template_type',
144 u'instance_type',
145 u'engine',
146 u'in_use',
147 u'filename'
148 ]
149
150 _suffix4engine = {
151 u'O': u'.ott',
152 u'L': u'.tex',
153 u'T': u'.txt',
154 u'X': u'.xslt',
155 u'I': u'.img',
156 u'P': u'.pdf'
157 }
158
159 #--------------------------------------------------------
161 """The template itself better not be arbitrarily large unless you can handle that.
162
163 Note that the data type returned will be a buffer."""
164
165 cmd = u'SELECT data FROM ref.paperwork_templates WHERE pk = %(pk)s'
166 rows, idx = gmPG2.run_ro_queries (queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}], get_col_idx = False)
167
168 if len(rows) == 0:
169 raise gmExceptions.NoSuchBusinessObjectError('cannot retrieve data for template pk = %s' % self.pk_obj)
170
171 return rows[0][0]
172
173 template_data = property(_get_template_data, lambda x:x)
174 #--------------------------------------------------------
176 """Export form template from database into file."""
177
178 if filename is None:
179 if self._payload[self._idx['filename']] is None:
180 suffix = self.__class__._suffix4engine[self._payload[self._idx['engine']]]
181 else:
182 suffix = os.path.splitext(self._payload[self._idx['filename']].strip())[1].strip()
183 if suffix in [u'', u'.']:
184 suffix = self.__class__._suffix4engine[self._payload[self._idx['engine']]]
185
186 filename = gmTools.get_unique_filename (
187 prefix = 'gm-%s-Template-' % self._payload[self._idx['engine']],
188 suffix = suffix
189 )
190
191 data_query = {
192 'cmd': u'SELECT substring(data from %(start)s for %(size)s) FROM ref.paperwork_templates WHERE pk = %(pk)s',
193 'args': {'pk': self.pk_obj}
194 }
195
196 data_size_query = {
197 'cmd': u'select octet_length(data) from ref.paperwork_templates where pk = %(pk)s',
198 'args': {'pk': self.pk_obj}
199 }
200
201 result = gmPG2.bytea2file (
202 data_query = data_query,
203 filename = filename,
204 data_size_query = data_size_query,
205 chunk_size = chunksize
206 )
207 if result is False:
208 return None
209
210 return filename
211 #--------------------------------------------------------
213 gmPG2.file2bytea (
214 filename = filename,
215 query = u'update ref.paperwork_templates set data = %(data)s::bytea where pk = %(pk)s and xmin = %(xmin)s',
216 args = {'pk': self.pk_obj, 'xmin': self._payload[self._idx['xmin_paperwork_template']]}
217 )
218 # adjust for xmin change
219 self.refetch_payload()
220 #--------------------------------------------------------
222 fname = self.export_to_file()
223 engine = form_engines[self._payload[self._idx['engine']]]
224 return engine(template_file = fname)
225 #============================================================
227 cmd = u'select pk from ref.paperwork_templates where name_long = %(lname)s and external_version = %(ver)s'
228 args = {'lname': name_long, 'ver': external_version}
229 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
230
231 if len(rows) == 0:
232 _log.error('cannot load form template [%s - %s]', name_long, external_version)
233 return None
234
235 return cFormTemplate(aPK_obj = rows[0]['pk'])
236 #------------------------------------------------------------
237 -def get_form_templates(engine=None, active_only=False, template_types=None, excluded_types=None):
238 """Load form templates."""
239
240 args = {'eng': engine, 'in_use': active_only}
241 where_parts = [u'1 = 1']
242
243 if engine is not None:
244 where_parts.append(u'engine = %(eng)s')
245
246 if active_only:
247 where_parts.append(u'in_use IS true')
248
249 if template_types is not None:
250 args['incl_types'] = tuple(template_types)
251 where_parts.append(u'template_type IN %(incl_types)s')
252
253 if excluded_types is not None:
254 args['excl_types'] = tuple(excluded_types)
255 where_parts.append(u'template_type NOT IN %(excl_types)s')
256
257 cmd = u"SELECT * FROM ref.v_paperwork_templates WHERE %s ORDER BY in_use desc, name_long" % u'\nAND '.join(where_parts)
258
259 rows, idx = gmPG2.run_ro_queries (
260 queries = [{'cmd': cmd, 'args': args}],
261 get_col_idx = True
262 )
263 templates = [ cFormTemplate(row = {'pk_field': 'pk_paperwork_template', 'data': r, 'idx': idx}) for r in rows ]
264
265 return templates
266 #------------------------------------------------------------
268
269 cmd = u'insert into ref.paperwork_templates (fk_template_type, name_short, name_long, external_version) values (%(type)s, %(nshort)s, %(nlong)s, %(ext_version)s)'
270 rows, idx = gmPG2.run_rw_queries (
271 queries = [
272 {'cmd': cmd, 'args': {'type': template_type, 'nshort': name_short, 'nlong': name_long, 'ext_version': 'new'}},
273 {'cmd': u"select currval(pg_get_serial_sequence('ref.paperwork_templates', 'pk'))"}
274 ],
275 return_data = True
276 )
277 template = cFormTemplate(aPK_obj = rows[0][0])
278 return template
279 #------------------------------------------------------------
281 rows, idx = gmPG2.run_rw_queries (
282 queries = [
283 {'cmd': u'delete from ref.paperwork_templates where pk=%(pk)s', 'args': {'pk': template['pk_paperwork_template']}}
284 ]
285 )
286 return True
287 #============================================================
288 # OpenOffice/LibreOffice API
289 #============================================================
290 uno = None
291 cOOoDocumentCloseListener = None
292 writer_binary = None
293
294 #-----------------------------------------------------------
296
297 try:
298 which = subprocess.Popen (
299 args = ('which', 'soffice'),
300 stdout = subprocess.PIPE,
301 stdin = subprocess.PIPE,
302 stderr = subprocess.PIPE,
303 universal_newlines = True
304 )
305 except (OSError, ValueError, subprocess.CalledProcessError):
306 _log.exception('there was a problem executing [which soffice]')
307 return
308
309 soffice_path, err = which.communicate()
310 soffice_path = soffice_path.strip('\n')
311 uno_path = os.path.abspath ( os.path.join (
312 os.path.dirname(os.path.realpath(soffice_path)),
313 '..',
314 'basis-link',
315 'program'
316 ))
317
318 _log.info('UNO should be at [%s], appending to sys.path', uno_path)
319
320 sys.path.append(uno_path)
321 #-----------------------------------------------------------
323 """FIXME: consider this:
324
325 try:
326 import uno
327 except:
328 print "This Script needs to be run with the python from OpenOffice.org"
329 print "Example: /opt/OpenOffice.org/program/python %s" % (
330 os.path.basename(sys.argv[0]))
331 print "Or you need to insert the right path at the top, where uno.py is."
332 print "Default: %s" % default_path
333 """
334 global uno
335 if uno is not None:
336 return
337
338 try:
339 import uno
340 except ImportError:
341 __configure_path_to_UNO()
342 import uno
343
344 global unohelper, oooXCloseListener, oooNoConnectException, oooPropertyValue
345
346 import unohelper
347 from com.sun.star.util import XCloseListener as oooXCloseListener
348 from com.sun.star.connection import NoConnectException as oooNoConnectException
349 from com.sun.star.beans import PropertyValue as oooPropertyValue
350
351 #----------------------------------
352 class _cOOoDocumentCloseListener(unohelper.Base, oooXCloseListener):
353 """Listens for events sent by OOo during the document closing
354 sequence and notifies the GNUmed client GUI so it can
355 import the closed document into the database.
356 """
357 def __init__(self, document=None):
358 self.document = document
359
360 def queryClosing(self, evt, owner):
361 # owner is True/False whether I am the owner of the doc
362 pass
363
364 def notifyClosing(self, evt):
365 pass
366
367 def disposing(self, evt):
368 self.document.on_disposed_by_ooo()
369 self.document = None
370 #----------------------------------
371
372 global cOOoDocumentCloseListener
373 cOOoDocumentCloseListener = _cOOoDocumentCloseListener
374
375 # search for writer binary
376 global writer_binary
377 found, binary = gmShellAPI.find_first_binary(binaries = [
378 'lowriter',
379 'oowriter'
380 ])
381 if found:
382 _log.debug('OOo/LO writer binary found: %s', binary)
383 writer_binary = binary
384 else:
385 _log.debug('OOo/LO writer binary NOT found')
386 raise ImportError('LibreOffice/OpenOffice (lowriter/oowriter) not found')
387
388 _log.debug('python UNO bridge successfully initialized')
389
390 #------------------------------------------------------------
392 """This class handles the connection to OOo.
393
394 Its Singleton instance stays around once initialized.
395 """
396 # FIXME: need to detect closure of OOo !
398
399 init_ooo()
400
401 #self.ooo_start_cmd = 'oowriter -invisible -accept="socket,host=localhost,port=2002;urp;"'
402 #self.remote_context_uri = "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext"
403
404 pipe_name = "uno-gm2lo-%s" % str(random.random())[2:]
405 _log.debug('pipe name: %s', pipe_name)
406
407 #self.ooo_start_cmd = '%s -invisible -norestore -accept="pipe,name=%s;urp"' % (
408 self.ooo_start_cmd = '%s --norestore --accept="pipe,name=%s;urp" &' % (
409 writer_binary,
410 pipe_name
411 )
412 _log.debug('startup command: %s', self.ooo_start_cmd)
413
414 self.remote_context_uri = "uno:pipe,name=%s;urp;StarOffice.ComponentContext" % pipe_name
415 _log.debug('remote context URI: %s', self.remote_context_uri)
416
417 self.resolver_uri = "com.sun.star.bridge.UnoUrlResolver"
418 self.desktop_uri = "com.sun.star.frame.Desktop"
419
420 self.local_context = uno.getComponentContext()
421 self.uri_resolver = self.local_context.ServiceManager.createInstanceWithContext(self.resolver_uri, self.local_context)
422
423 self.__desktop = None
424 #--------------------------------------------------------
426 if self.__desktop is None:
427 _log.debug('no desktop, no cleanup')
428 return
429
430 try:
431 self.__desktop.terminate()
432 except:
433 _log.exception('cannot terminate OOo desktop')
434 #--------------------------------------------------------
436 """<filename> must be absolute"""
437
438 if self.desktop is None:
439 _log.error('cannot access OOo desktop')
440 return None
441
442 filename = os.path.expanduser(filename)
443 filename = os.path.abspath(filename)
444 document_uri = uno.systemPathToFileUrl(filename)
445
446 _log.debug('%s -> %s', filename, document_uri)
447
448 doc = self.desktop.loadComponentFromURL(document_uri, "_blank", 0, ())
449 return doc
450 #--------------------------------------------------------
451 # internal helpers
452 #--------------------------------------------------------
454 # later factor this out !
455 dbcfg = gmCfg.cCfgSQL()
456 self.ooo_startup_settle_time = dbcfg.get2 (
457 option = u'external.ooo.startup_settle_time',
458 workplace = gmSurgery.gmCurrentPractice().active_workplace,
459 bias = u'workplace',
460 default = 3.0
461 )
462 #--------------------------------------------------------
463 # properties
464 #--------------------------------------------------------
466 if self.__desktop is not None:
467 return self.__desktop
468
469 try:
470 self.remote_context = self.uri_resolver.resolve(self.remote_context_uri)
471 except oooNoConnectException:
472 _log.exception('cannot connect to OOo server')
473 _log.info('trying to start OOo server')
474 os.system(self.ooo_start_cmd)
475 self.__get_startup_settle_time()
476 _log.debug('waiting %s seconds for OOo to start up', self.ooo_startup_settle_time)
477 time.sleep(self.ooo_startup_settle_time) # OOo sometimes needs a bit
478 try:
479 self.remote_context = self.uri_resolver.resolve(self.remote_context_uri)
480 except oooNoConnectException:
481 _log.exception('cannot start (or connect to started) OOo server')
482 return None
483
484 self.__desktop = self.remote_context.ServiceManager.createInstanceWithContext(self.desktop_uri, self.remote_context)
485 _log.debug('connection seems established')
486 return self.__desktop
487
488 desktop = property(_get_desktop, lambda x:x)
489 #------------------------------------------------------------
491
493
494 self.template_file = template_file
495 self.instance_type = instance_type
496 self.ooo_doc = None
497 #--------------------------------------------------------
498 # external API
499 #--------------------------------------------------------
501 # connect to OOo
502 ooo_srv = gmOOoConnector()
503
504 # open doc in OOo
505 self.ooo_doc = ooo_srv.open_document(filename = self.template_file)
506 if self.ooo_doc is None:
507 _log.error('cannot open document in OOo')
508 return False
509
510 # listen for close events
511 pat = gmPerson.gmCurrentPatient()
512 pat.locked = True
513 listener = cOOoDocumentCloseListener(document = self)
514 self.ooo_doc.addCloseListener(listener)
515
516 return True
517 #--------------------------------------------------------
520 #--------------------------------------------------------
522
523 # new style embedded, implicit placeholders
524 searcher = self.ooo_doc.createSearchDescriptor()
525 searcher.SearchCaseSensitive = False
526 searcher.SearchRegularExpression = True
527 searcher.SearchWords = True
528 searcher.SearchString = handler.placeholder_regex
529
530 placeholder_instance = self.ooo_doc.findFirst(searcher)
531 while placeholder_instance is not None:
532 try:
533 val = handler[placeholder_instance.String]
534 except:
535 _log.exception(val)
536 val = _('error with placeholder [%s]') % placeholder_instance.String
537
538 if val is None:
539 val = _('error with placeholder [%s]') % placeholder_instance.String
540
541 placeholder_instance.String = val
542 placeholder_instance = self.ooo_doc.findNext(placeholder_instance.End, searcher)
543
544 if not old_style_too:
545 return
546
547 # old style "explicit" placeholders
548 text_fields = self.ooo_doc.getTextFields().createEnumeration()
549 while text_fields.hasMoreElements():
550 text_field = text_fields.nextElement()
551
552 # placeholder ?
553 if not text_field.supportsService('com.sun.star.text.TextField.JumpEdit'):
554 continue
555 # placeholder of type text ?
556 if text_field.PlaceHolderType != 0:
557 continue
558
559 replacement = handler[text_field.PlaceHolder]
560 if replacement is None:
561 continue
562
563 text_field.Anchor.setString(replacement)
564 #--------------------------------------------------------
566 if filename is not None:
567 target_url = uno.systemPathToFileUrl(os.path.abspath(os.path.expanduser(filename)))
568 save_args = (
569 oooPropertyValue('Overwrite', 0, True, 0),
570 oooPropertyValue('FormatFilter', 0, 'swriter: StarOffice XML (Writer)', 0)
571
572 )
573 # "store AS url" stores the doc, marks it unmodified and updates
574 # the internal media descriptor - as opposed to "store TO url"
575 self.ooo_doc.storeAsURL(target_url, save_args)
576 else:
577 self.ooo_doc.store()
578 #--------------------------------------------------------
580 self.ooo_doc.dispose()
581 pat = gmPerson.gmCurrentPatient()
582 pat.locked = False
583 self.ooo_doc = None
584 #--------------------------------------------------------
586 # get current file name from OOo, user may have used Save As
587 filename = uno.fileUrlToSystemPath(self.ooo_doc.URL)
588 # tell UI to import the file
589 gmDispatcher.send (
590 signal = u'import_document_from_file',
591 filename = filename,
592 document_type = self.instance_type,
593 unlock_patient = True
594 )
595 self.ooo_doc = None
596 #--------------------------------------------------------
597 # internal helpers
598 #--------------------------------------------------------
599
600 #============================================================
602 """Ancestor for forms."""
603
606 #--------------------------------------------------------
608 """Parse the template into an instance and replace placeholders with values."""
609 raise NotImplementedError
610 #--------------------------------------------------------
614 #--------------------------------------------------------
616 """Generate output suitable for further processing outside this class, e.g. printing."""
617 raise NotImplementedError
618 #--------------------------------------------------------
623 #--------------------------------------------------------
625 """
626 A sop to TeX which can't act as a true filter: to delete temporary files
627 """
628 pass
629 #--------------------------------------------------------
631 """
632 Executes the provided command.
633 If command cotains %F. it is substituted with the filename
634 Otherwise, the file is fed in on stdin
635 """
636 pass
637 #--------------------------------------------------------
639 """Stores the parameters in the backend.
640
641 - link_obj can be a cursor, a connection or a service name
642 - assigning a cursor to link_obj allows the calling code to
643 group the call to store() into an enclosing transaction
644 (for an example see gmReferral.send_referral()...)
645 """
646 # some forms may not have values ...
647 if params is None:
648 params = {}
649 patient_clinical = self.patient.get_emr()
650 encounter = patient_clinical.active_encounter['pk_encounter']
651 # FIXME: get_active_episode is no more
652 #episode = patient_clinical.get_active_episode()['pk_episode']
653 # generate "forever unique" name
654 cmd = "select name_short || ': <' || name_long || '::' || external_version || '>' from paperwork_templates where pk=%s";
655 rows = gmPG.run_ro_query('reference', cmd, None, self.pk_def)
656 form_name = None
657 if rows is None:
658 _log.error('error retrieving form def for [%s]' % self.pk_def)
659 elif len(rows) == 0:
660 _log.error('no form def for [%s]' % self.pk_def)
661 else:
662 form_name = rows[0][0]
663 # we didn't get a name but want to store the form anyhow
664 if form_name is None:
665 form_name=time.time() # hopefully unique enough
666 # in one transaction
667 queries = []
668 # - store form instance in form_instance
669 cmd = "insert into form_instances(fk_form_def, form_name, fk_episode, fk_encounter) values (%s, %s, %s, %s)"
670 queries.append((cmd, [self.pk_def, form_name, episode, encounter]))
671 # - store params in form_data
672 for key in params.keys():
673 cmd = """
674 insert into form_data(fk_instance, place_holder, value)
675 values ((select currval('form_instances_pk_seq')), %s, %s::text)
676 """
677 queries.append((cmd, [key, params[key]]))
678 # - get inserted PK
679 queries.append(("select currval ('form_instances_pk_seq')", []))
680 status, err = gmPG.run_commit('historica', queries, True)
681 if status is None:
682 _log.error('failed to store form [%s] (%s): %s' % (self.pk_def, form_name, err))
683 return None
684 return status
685
686 #================================================================
687 # OOo template forms
688 #----------------------------------------------------------------
690 """A forms engine wrapping OOo."""
691
693 super(self.__class__, self).__init__(template_file = template_file)
694
695
696 path, ext = os.path.splitext(self.template_filename)
697 if ext in [r'', r'.']:
698 ext = r'.odt'
699 self.instance_filename = r'%s-instance%s' % (path, ext)
700
701 #================================================================
702 # LaTeX template forms
703 #----------------------------------------------------------------
705 """A forms engine wrapping LaTeX."""
706
708 super(self.__class__, self).__init__(template_file = template_file)
709 path, ext = os.path.splitext(self.template_filename)
710 if ext in [r'', r'.']:
711 ext = r'.tex'
712 self.instance_filename = r'%s-instance%s' % (path, ext)
713 #--------------------------------------------------------
715
716 template_file = codecs.open(self.template_filename, 'rU', 'utf8')
717 instance_file = codecs.open(self.instance_filename, 'wb', 'utf8')
718
719 for line in template_file:
720
721 if line.strip() in [u'', u'\r', u'\n', u'\r\n']:
722 instance_file.write(line)
723 continue
724
725 # 1) find placeholders in this line
726 placeholders_in_line = regex.findall(data_source.placeholder_regex, line, regex.IGNORECASE)
727 # 2) and replace them
728 for placeholder in placeholders_in_line:
729 try:
730 val = data_source[placeholder]
731 except:
732 _log.exception(val)
733 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder)
734
735 if val is None:
736 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder)
737
738 line = line.replace(placeholder, val)
739
740 instance_file.write(line)
741
742 instance_file.close()
743 template_file.close()
744
745 return
746 #--------------------------------------------------------
748
749 mimetypes = [
750 u'application/x-latex',
751 u'application/x-tex',
752 u'text/plain'
753 ]
754
755 for mimetype in mimetypes:
756 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename)
757 if editor_cmd is not None:
758 break
759
760 if editor_cmd is None:
761 editor_cmd = u'sensible-editor %s' % self.instance_filename
762
763 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True)
764 self.re_editable_filenames = [self.instance_filename]
765
766 return result
767 #--------------------------------------------------------
769
770 if instance_file is None:
771 instance_file = self.instance_filename
772
773 try:
774 open(instance_file, 'r').close()
775 except:
776 _log.exception('cannot access form instance file [%s]', instance_file)
777 gmLog2.log_stack_trace()
778 return None
779
780 self.instance_filename = instance_file
781
782 _log.debug('ignoring <format> directive [%s], generating PDF', format)
783
784 # create sandbox for LaTeX to play in
785 sandbox_dir = os.path.splitext(self.template_filename)[0]
786 _log.debug('LaTeX sandbox directory: [%s]', sandbox_dir)
787
788 old_cwd = os.getcwd()
789 _log.debug('CWD: [%s]', old_cwd)
790
791 gmTools.mkdir(sandbox_dir)
792
793 os.chdir(sandbox_dir)
794 try:
795 sandboxed_instance_filename = os.path.join(sandbox_dir, os.path.split(self.instance_filename)[1])
796 shutil.move(self.instance_filename, sandboxed_instance_filename)
797
798 # LaTeX can need up to three runs to get cross references et al right
799 if platform.system() == 'Windows':
800 draft_cmd = r'pdflatex.exe -draftmode -interaction nonstopmode %s' % sandboxed_instance_filename
801 final_cmd = r'pdflatex.exe -interaction nonstopmode %s' % sandboxed_instance_filename
802 else:
803 draft_cmd = r'pdflatex -draftmode -interaction nonstopmode %s' % sandboxed_instance_filename
804 final_cmd = r'pdflatex -interaction nonstopmode %s' % sandboxed_instance_filename
805 for run_cmd in [draft_cmd, draft_cmd, final_cmd]:
806 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]):
807 _log.error('problem running pdflatex, cannot generate form output')
808 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdflatex. Cannot turn LaTeX template into PDF.'), beep = True)
809 os.chdir(old_cwd)
810 return None
811 finally:
812 os.chdir(old_cwd)
813
814 sandboxed_pdf_name = u'%s.pdf' % os.path.splitext(sandboxed_instance_filename)[0]
815 target_dir = os.path.split(self.instance_filename)[0]
816 try:
817 shutil.move(sandboxed_pdf_name, target_dir)
818 except IOError:
819 _log.exception('cannot move sandboxed PDF: %s -> %s', sandboxed_pdf_name, target_dir)
820 gmDispatcher.send(signal = 'statustext', msg = _('Sandboxed PDF output file cannot be moved.'), beep = True)
821 return None
822
823 final_pdf_name = u'%s.pdf' % os.path.splitext(self.instance_filename)[0]
824
825 try:
826 open(final_pdf_name, 'r').close()
827 except IOError:
828 _log.exception('cannot open target PDF: %s', final_pdf_name)
829 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True)
830 return None
831
832 self.final_output_filenames = [final_pdf_name]
833
834 return final_pdf_name
835 #------------------------------------------------------------
836 form_engines[u'L'] = cLaTeXForm
837 #============================================================
838 # Gnuplot template forms
839 #------------------------------------------------------------
841 """A forms engine wrapping Gnuplot."""
842
843 #--------------------------------------------------------
847 #--------------------------------------------------------
849 """Allow editing the instance of the template."""
850 self.re_editable_filenames = []
851 return True
852 #--------------------------------------------------------
854 """Generate output suitable for further processing outside this class, e.g. printing.
855
856 Expects .data_filename to be set.
857 """
858 self.conf_filename = gmTools.get_unique_filename(prefix = 'gm2gpl-', suffix = '.conf')
859 fname_file = codecs.open(self.conf_filename, 'wb', 'utf8')
860 fname_file.write('# setting the gnuplot data file\n')
861 fname_file.write("gm2gpl_datafile = '%s'\n" % self.data_filename)
862 fname_file.close()
863
864 # FIXME: cater for configurable path
865 if platform.system() == 'Windows':
866 exec_name = 'gnuplot.exe'
867 else:
868 exec_name = 'gnuplot'
869
870 args = [exec_name, '-p', self.conf_filename, self.template_filename]
871 _log.debug('plotting args: %s' % str(args))
872
873 try:
874 gp = subprocess.Popen (
875 args = args,
876 close_fds = True
877 )
878 except (OSError, ValueError, subprocess.CalledProcessError):
879 _log.exception('there was a problem executing gnuplot')
880 gmDispatcher.send(signal = u'statustext', msg = _('Error running gnuplot. Cannot plot data.'), beep = True)
881 return
882
883 gp.communicate()
884
885 self.final_output_filenames = [
886 self.conf_filename,
887 self.data_filename,
888 self.template_filename
889 ]
890
891 return
892 #------------------------------------------------------------
893 form_engines[u'G'] = cGnuplotForm
894
895 #============================================================
896 # fPDF form engine
897 #------------------------------------------------------------
899 """A forms engine wrapping PDF forms.
900
901 Johann Felix Soden <johfel@gmx.de> helped with this.
902
903 http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf
904
905 http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/fdf_data_exchange.pdf
906 """
907
909
910 super(cPDFForm, self).__init__(template_file = template_file)
911
912 # detect pdftk
913 found, self.pdftk_binary = gmShellAPI.detect_external_binary(binary = r'pdftk')
914 if not found:
915 raise ImportError('<pdftk(.exe)> not found')
916 return # should be superfluous, actually
917
918 enc = sys.getfilesystemencoding()
919 self.pdftk_binary = self.pdftk_binary.encode(enc)
920
921 base_name, ext = os.path.splitext(self.template_filename)
922 self.fdf_dumped_filename = (u'%s.fdf' % base_name).encode(enc)
923 self.fdf_replaced_filename = (u'%s-replaced.fdf' % base_name).encode(enc)
924 self.pdf_filled_filename = (u'%s-filled.pdf' % base_name).encode(enc)
925 self.pdf_flattened_filename = (u'%s-filled-flattened.pdf' % base_name).encode(enc)
926 #--------------------------------------------------------
928
929 # dump form fields from template
930 cmd_line = [
931 self.pdftk_binary,
932 self.template_filename,
933 r'generate_fdf',
934 r'output',
935 self.fdf_dumped_filename
936 ]
937 _log.debug(u' '.join(cmd_line))
938 try:
939 pdftk = subprocess.Popen(cmd_line)
940 except OSError:
941 _log.exception('cannot run <pdftk> (dump data from form)')
942 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot extract fields from PDF form template.'), beep = True)
943 return False
944
945 pdftk.communicate()
946 if pdftk.returncode != 0:
947 _log.error('<pdftk> returned [%s], failed to dump data from PDF form into FDF', pdftk.returncode)
948 return False
949
950 # parse dumped FDF file for "/V (...)" records
951 # and replace placeholders therein
952 fdf_dumped_file = open(self.fdf_dumped_filename, 'rbU')
953 fdf_replaced_file = codecs.open(self.fdf_replaced_filename, 'wb')
954
955 string_value_regex = r'\s*/V\s*\(.+\)\s*$'
956 for line in fdf_dumped_file:
957 if not regex.match(string_value_regex, line):
958 fdf_replaced_file.write(line)
959 continue
960
961 # strip cruft around the string value
962 raw_str_val = line.strip() # remove framing whitespace
963 raw_str_val = raw_str_val[2:] # remove leading "/V"
964 raw_str_val = raw_str_val.lstrip() # remove whitespace between "/V" and "("
965 raw_str_val = raw_str_val[1:] # remove opening "("
966 raw_str_val = raw_str_val[2:] # remove BOM-16-BE
967 raw_str_val = raw_str_val.rstrip() # remove trailing whitespace
968 raw_str_val = raw_str_val[:-1] # remove closing ")"
969
970 # work on FDF escapes
971 raw_str_val = raw_str_val.replace('\(', '(') # remove escaping of "("
972 raw_str_val = raw_str_val.replace('\)', ')') # remove escaping of ")"
973
974 # by now raw_str_val should contain the actual
975 # string value, albeit encoded as UTF-16, so
976 # decode it into a unicode object,
977 # split multi-line fields on "\n" literal
978 raw_str_lines = raw_str_val.split('\x00\\n')
979 value_template_lines = []
980 for raw_str_line in raw_str_lines:
981 value_template_lines.append(raw_str_line.decode('utf_16_be'))
982
983 replaced_lines = []
984 for value_template in value_template_lines:
985 # find any placeholders within
986 placeholders_in_value = regex.findall(data_source.placeholder_regex, value_template, regex.IGNORECASE)
987 for placeholder in placeholders_in_value:
988 try:
989 replacement = data_source[placeholder]
990 except:
991 _log.exception(replacement)
992 replacement = _('error with placeholder [%s]') % placeholder
993 if replacement is None:
994 replacement = _('error with placeholder [%s]') % placeholder
995 value_template = value_template.replace(placeholder, replacement)
996
997 value_template = value_template.encode('utf_16_be')
998
999 if len(placeholders_in_value) > 0:
1000 value_template = value_template.replace(r'(', r'\(')
1001 value_template = value_template.replace(r')', r'\)')
1002
1003 replaced_lines.append(value_template)
1004
1005 replaced_line = '\x00\\n'.join(replaced_lines)
1006
1007 fdf_replaced_file.write('/V (')
1008 fdf_replaced_file.write(codecs.BOM_UTF16_BE)
1009 fdf_replaced_file.write(replaced_line)
1010 fdf_replaced_file.write(')\n')
1011
1012 fdf_replaced_file.close()
1013 fdf_dumped_file.close()
1014
1015 # merge replaced data back into form
1016 cmd_line = [
1017 self.pdftk_binary,
1018 self.template_filename,
1019 r'fill_form',
1020 self.fdf_replaced_filename,
1021 r'output',
1022 self.pdf_filled_filename
1023 ]
1024 _log.debug(u' '.join(cmd_line))
1025 try:
1026 pdftk = subprocess.Popen(cmd_line)
1027 except OSError:
1028 _log.exception('cannot run <pdftk> (merge data into form)')
1029 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot fill in PDF form template.'), beep = True)
1030 return False
1031
1032 pdftk.communicate()
1033 if pdftk.returncode != 0:
1034 _log.error('<pdftk> returned [%s], failed to merge FDF data into PDF form', pdftk.returncode)
1035 return False
1036
1037 return True
1038 #--------------------------------------------------------
1040 mimetypes = [
1041 u'application/pdf',
1042 u'application/x-pdf'
1043 ]
1044
1045 for mimetype in mimetypes:
1046 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.pdf_filled_filename)
1047 if editor_cmd is not None:
1048 break
1049
1050 if editor_cmd is None:
1051 _log.debug('editor cmd not found, trying viewer cmd')
1052 for mimetype in mimetypes:
1053 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.pdf_filled_filename)
1054 if editor_cmd is not None:
1055 break
1056
1057 if editor_cmd is None:
1058 return False
1059
1060 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True)
1061
1062 path, fname = os.path.split(self.pdf_filled_filename)
1063 candidate = os.path.join(gmTools.gmPaths().home_dir, fname)
1064
1065 if os.access(candidate, os.R_OK):
1066 _log.debug('filled-in PDF found: %s', candidate)
1067 os.rename(self.pdf_filled_filename, self.pdf_filled_filename + '.bak')
1068 shutil.move(candidate, path)
1069 else:
1070 _log.debug('filled-in PDF not found: %s', candidate)
1071
1072 self.re_editable_filenames = [self.pdf_filled_filename]
1073
1074 return result
1075 #--------------------------------------------------------
1077 """Generate output suitable for further processing outside this class, e.g. printing."""
1078
1079 # eventually flatten the filled in form so we
1080 # can keep both a flattened and an editable copy:
1081 cmd_line = [
1082 self.pdftk_binary,
1083 self.pdf_filled_filename,
1084 r'output',
1085 self.pdf_flattened_filename,
1086 r'flatten'
1087 ]
1088 _log.debug(u' '.join(cmd_line))
1089 try:
1090 pdftk = subprocess.Popen(cmd_line)
1091 except OSError:
1092 _log.exception('cannot run <pdftk> (flatten filled in form)')
1093 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot flatten filled in PDF form.'), beep = True)
1094 return None
1095
1096 pdftk.communicate()
1097 if pdftk.returncode != 0:
1098 _log.error('<pdftk> returned [%s], failed to flatten filled in PDF form', pdftk.returncode)
1099 return None
1100
1101 self.final_output_filenames = [self.pdf_flattened_filename]
1102
1103 return self.pdf_flattened_filename
1104 #------------------------------------------------------------
1105 form_engines[u'P'] = cPDFForm
1106
1107 #============================================================
1108 # older code
1109 #------------------------------------------------------------
1111 """A forms engine wrapping LaTeX.
1112 """
1116
1118 try:
1119 latex = Cheetah.Template.Template (self.template, filter=LaTeXFilter, searchList=[params])
1120 # create a 'sandbox' directory for LaTeX to play in
1121 self.tmp = tempfile.mktemp ()
1122 os.makedirs (self.tmp)
1123 self.oldcwd = os.getcwd ()
1124 os.chdir (self.tmp)
1125 stdin = os.popen ("latex", "w", 2048)
1126 stdin.write (str (latex)) #send text. LaTeX spits it's output into stdout
1127 # FIXME: send LaTeX output to the logger
1128 stdin.close ()
1129 if not gmShellAPI.run_command_in_shell("dvips texput.dvi -o texput.ps", blocking=True):
1130 raise FormError ('DVIPS returned error')
1131 except EnvironmentError, e:
1132 _log.error(e.strerror)
1133 raise FormError (e.strerror)
1134 return file ("texput.ps")
1135
1137 """
1138 For testing purposes, runs Xdvi on the intermediate TeX output
1139 WARNING: don't try this on Windows
1140 """
1141 gmShellAPI.run_command_in_shell("xdvi texput.dvi", blocking=True)
1142
1144 if "%F" in command:
1145 command.replace ("%F", "texput.ps")
1146 else:
1147 command = "%s < texput.ps" % command
1148 try:
1149 if not gmShellAPI.run_command_in_shell(command, blocking=True):
1150 _log.error("external command %s returned non-zero" % command)
1151 raise FormError ('external command %s returned error' % command)
1152 except EnvironmentError, e:
1153 _log.error(e.strerror)
1154 raise FormError (e.strerror)
1155 return True
1156
1158 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print')
1159 self.exe (command)
1160
1169
1170
1171
1172
1173 #================================================================
1174 # define a class for HTML forms (for printing)
1175 #================================================================
1177 """This class can create XML document from requested data,
1178 then process it with XSLT template and display results
1179 """
1180
1181 # FIXME: make the path configurable ?
1182 _preview_program = u'oowriter ' #this program must be in the system PATH
1183
1185
1186 if template is None:
1187 raise ValueError(u'%s: cannot create form instance without a template' % __name__)
1188
1189 cFormEngine.__init__(self, template = template)
1190
1191 self._FormData = None
1192
1193 # here we know/can assume that the template was stored as a utf-8
1194 # encoded string so use that conversion to create unicode:
1195 #self._XSLTData = unicode(str(template.template_data), 'UTF-8')
1196 # but in fact, unicode() knows how to handle buffers, so simply:
1197 self._XSLTData = unicode(self.template.template_data, 'UTF-8', 'strict')
1198
1199 # we must still devise a method of extracting the SQL query:
1200 # - either by retrieving it from a particular tag in the XSLT or
1201 # - by making the stored template actually be a dict which, unpickled,
1202 # has the keys "xslt" and "sql"
1203 self._SQL_query = u'select 1' #this sql query must output valid xml
1204 #--------------------------------------------------------
1205 # external API
1206 #--------------------------------------------------------
1208 """get data from backend and process it with XSLT template to produce readable output"""
1209
1210 # extract SQL (this is wrong but displays what is intended)
1211 xslt = libxml2.parseDoc(self._XSLTData)
1212 root = xslt.children
1213 for child in root:
1214 if child.type == 'element':
1215 self._SQL_query = child.content
1216 break
1217
1218 # retrieve data from backend
1219 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': self._SQL_query, 'args': sql_parameters}], get_col_idx = False)
1220
1221 __header = '<?xml version="1.0" encoding="UTF-8"?>\n'
1222 __body = rows[0][0]
1223
1224 # process XML data according to supplied XSLT, producing HTML
1225 self._XMLData =__header + __body
1226 style = libxslt.parseStylesheetDoc(xslt)
1227 xml = libxml2.parseDoc(self._XMLData)
1228 html = style.applyStylesheet(xml, None)
1229 self._FormData = html.serialize()
1230
1231 style.freeStylesheet()
1232 xml.freeDoc()
1233 html.freeDoc()
1234 #--------------------------------------------------------
1236 if self._FormData is None:
1237 raise ValueError, u'Preview request for empty form. Make sure the form is properly initialized and process() was performed'
1238
1239 fname = gmTools.get_unique_filename(prefix = u'gm_XSLT_form-', suffix = u'.html')
1240 #html_file = os.open(fname, 'wb')
1241 #html_file.write(self._FormData.encode('UTF-8'))
1242 html_file = codecs.open(fname, 'wb', 'utf8', 'strict') # or 'replace' ?
1243 html_file.write(self._FormData)
1244 html_file.close()
1245
1246 cmd = u'%s %s' % (self.__class__._preview_program, fname)
1247
1248 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = False):
1249 _log.error('%s: cannot launch report preview program' % __name__)
1250 return False
1251
1252 #os.unlink(self.filename) #delete file
1253 #FIXME: under Windows the temp file is deleted before preview program gets it (under Linux it works OK)
1254
1255 return True
1256 #--------------------------------------------------------
1260
1261
1262 #=====================================================
1263 #class LaTeXFilter(Cheetah.Filters.Filter):
1266 """
1267 Convience function to escape ISO-Latin-1 strings for TeX output
1268 WARNING: not all ISO-Latin-1 characters are expressible in TeX
1269 FIXME: nevertheless, there are a few more we could support
1270
1271 Also intelligently convert lists and tuples into TeX-style table lines
1272 """
1273 if type (item) is types.UnicodeType or type (item) is types.StringType:
1274 item = item.replace ("\\", "\\backslash") # I wonder about this, do we want users to be able to use raw TeX?
1275 item = item.replace ("&", "\\&")
1276 item = item.replace ("$", "\\$")
1277 item = item.replace ('"', "") # okay, that's not right, but easiest solution for now
1278 item = item.replace ("\n", "\\\\ ")
1279 if len (item.strip ()) == 0:
1280 item = "\\relax " # sometimes TeX really hates empty strings, this seems to mollify it
1281 # FIXME: cover all of ISO-Latin-1 which can be expressed in TeX
1282 if type (item) is types.UnicodeType:
1283 item = item.encode ('latin-1', 'replace')
1284 trans = {'ß':'\\ss{}', 'ä': '\\"{a}', 'Ä' :'\\"{A}', 'ö': '\\"{o}', 'Ö': '\\"{O}', 'ü': '\\"{u}', 'Ü': '\\"{U}',
1285 '\x8a':'\\v{S}', '\x8a':'\\OE{}', '\x9a':'\\v{s}', '\x9c': '\\oe{}', '\a9f':'\\"{Y}', #Microsloth extensions
1286 '\x86': '{\\dag}', '\x87': '{\\ddag}', '\xa7':'{\\S}', '\xb6': '{\\P}', '\xa9': '{\\copyright}', '\xbf': '?`',
1287 '\xc0':'\\`{A}', '\xa1': "\\'{A}", '\xa2': '\\^{A}', '\xa3':'\\~{A}', '\\xc5': '{\AA}',
1288 '\xc7':'\\c{C}', '\xc8':'\\`{E}',
1289 '\xa1': '!`',
1290 '\xb5':'$\mu$', '\xa3': '\pounds{}', '\xa2':'cent'}
1291 for k, i in trans.items ():
1292 item = item.replace (k, i)
1293 elif type (item) is types.ListType or type (item) is types.TupleType:
1294 item = string.join ([self.filter (i, ' & ') for i in item], table_sep)
1295 elif item is None:
1296 item = '\\relax % Python None\n'
1297 elif type (item) is types.IntType or type (item) is types.FloatType:
1298 item = str (item)
1299 else:
1300 item = str (item)
1301 _log.warning("unknown type %s, string %s" % (type (item), item))
1302 return item
1303
1304
1305 #===========================================================
1308
1309 #============================================================
1310 # convenience functions
1311 #------------------------------------------------------------
1313 """
1314 Instantiates a FormEngine based on the form ID or name from the backend
1315 """
1316 try:
1317 # it's a number: match to form ID
1318 id = int (id)
1319 cmd = 'select template, engine, pk from paperwork_templates where pk = %s'
1320 except ValueError:
1321 # it's a string, match to the form's name
1322 # FIXME: can we somehow OR like this: where name_short=%s OR name_long=%s ?
1323 cmd = 'select template, engine, flags, pk from paperwork_templates where name_short = %s'
1324 result = gmPG.run_ro_query ('reference', cmd, None, id)
1325 if result is None:
1326 _log.error('error getting form [%s]' % id)
1327 raise gmExceptions.FormError ('error getting form [%s]' % id)
1328 if len(result) == 0:
1329 _log.error('no form [%s] found' % id)
1330 raise gmExceptions.FormError ('no such form found [%s]' % id)
1331 if result[0][1] == 'L':
1332 return LaTeXForm (result[0][2], result[0][0])
1333 elif result[0][1] == 'T':
1334 return TextForm (result[0][2], result[0][0])
1335 else:
1336 _log.error('no form engine [%s] for form [%s]' % (result[0][1], id))
1337 raise FormError ('no engine [%s] for form [%s]' % (result[0][1], id))
1338 #-------------------------------------------------------------
1345 #-------------------------------------------------------------
1346
1347 test_letter = """
1348 \\documentclass{letter}
1349 \\address{ $DOCTOR \\\\
1350 $DOCTORADDRESS}
1351 \\signature{$DOCTOR}
1352
1353 \\begin{document}
1354 \\begin{letter}{$RECIPIENTNAME \\\\
1355 $RECIPIENTADDRESS}
1356
1357 \\opening{Dear $RECIPIENTNAME}
1358
1359 \\textbf{Re:} $PATIENTNAME, DOB: $DOB, $PATIENTADDRESS \\\\
1360
1361 $TEXT
1362
1363 \\ifnum$INCLUDEMEDS>0
1364 \\textbf{Medications List}
1365
1366 \\begin{tabular}{lll}
1367 $MEDSLIST
1368 \\end{tabular}
1369 \\fi
1370
1371 \\ifnum$INCLUDEDISEASES>0
1372 \\textbf{Disease List}
1373
1374 \\begin{tabular}{l}
1375 $DISEASELIST
1376 \\end{tabular}
1377 \\fi
1378
1379 \\closing{$CLOSING}
1380
1381 \\end{letter}
1382 \\end{document}
1383 """
1384
1385
1387 f = open('../../test-area/ian/terry-form.tex')
1388 params = {
1389 'RECIPIENT': "Dr. R. Terry\n1 Main St\nNewcastle",
1390 'DOCTORSNAME': 'Ian Haywood',
1391 'DOCTORSADDRESS': '1 Smith St\nMelbourne',
1392 'PATIENTNAME':'Joe Bloggs',
1393 'PATIENTADDRESS':'18 Fred St\nMelbourne',
1394 'REQUEST':'echocardiogram',
1395 'THERAPY':'on warfarin',
1396 'CLINICALNOTES':"""heard new murmur
1397 Here's some
1398 crap to demonstrate how it can cover multiple lines.""",
1399 'COPYADDRESS':'Karsten Hilbert\nLeipzig, Germany',
1400 'ROUTINE':1,
1401 'URGENT':0,
1402 'FAX':1,
1403 'PHONE':1,
1404 'PENSIONER':1,
1405 'VETERAN':0,
1406 'PADS':0,
1407 'INSTRUCTIONS':u'Take the blue pill, Neo'
1408 }
1409 form = LaTeXForm (1, f.read())
1410 form.process (params)
1411 form.xdvi ()
1412 form.cleanup ()
1413
1415 form = LaTeXForm (2, test_letter)
1416 params = {'RECIPIENTNAME':'Dr. Richard Terry',
1417 'RECIPIENTADDRESS':'1 Main St\nNewcastle',
1418 'DOCTOR':'Dr. Ian Haywood',
1419 'DOCTORADDRESS':'1 Smith St\nMelbourne',
1420 'PATIENTNAME':'Joe Bloggs',
1421 'PATIENTADDRESS':'18 Fred St, Melbourne',
1422 'TEXT':"""This is the main text of the referral letter""",
1423 'DOB':'12/3/65',
1424 'INCLUDEMEDS':1,
1425 'MEDSLIST':[["Amoxycillin", "500mg", "TDS"], ["Perindopril", "4mg", "OD"]],
1426 'INCLUDEDISEASES':0, 'DISEASELIST':'',
1427 'CLOSING':'Yours sincerely,'
1428 }
1429 form.process (params)
1430 print os.getcwd ()
1431 form.xdvi ()
1432 form.cleanup ()
1433 #------------------------------------------------------------
1435 template = open('../../test-area/ian/Formularkopf-DE.tex')
1436 form = LaTeXForm(template=template.read())
1437 params = {
1438 'PATIENT LASTNAME': 'Kirk',
1439 'PATIENT FIRSTNAME': 'James T.',
1440 'PATIENT STREET': 'Hauptstrasse',
1441 'PATIENT ZIP': '02999',
1442 'PATIENT TOWN': 'Gross Saerchen',
1443 'PATIENT DOB': '22.03.1931'
1444 }
1445 form.process(params)
1446 form.xdvi()
1447 form.cleanup()
1448
1449 #============================================================
1450 # main
1451 #------------------------------------------------------------
1452 if __name__ == '__main__':
1453
1454 if len(sys.argv) < 2:
1455 sys.exit()
1456
1457 if sys.argv[1] != 'test':
1458 sys.exit()
1459
1460 from Gnumed.pycommon import gmI18N, gmDateTime
1461 gmI18N.activate_locale()
1462 gmI18N.install_domain(domain='gnumed')
1463 gmDateTime.init()
1464
1465 #--------------------------------------------------------
1466 # OOo
1467 #--------------------------------------------------------
1469 init_ooo()
1470 #--------------------------------------------------------
1475 #--------------------------------------------------------
1477 srv = gmOOoConnector()
1478 doc = srv.open_document(filename = sys.argv[2])
1479 print "document:", doc
1480 #--------------------------------------------------------
1482 doc = cOOoLetter(template_file = sys.argv[2])
1483 doc.open_in_ooo()
1484 print "document:", doc
1485 raw_input('press <ENTER> to continue')
1486 doc.show()
1487 #doc.replace_placeholders()
1488 #doc.save_in_ooo('~/test_cOOoLetter.odt')
1489 # doc = None
1490 # doc.close_in_ooo()
1491 raw_input('press <ENTER> to continue')
1492 #--------------------------------------------------------
1494 try:
1495 doc = open_uri_in_ooo(filename=sys.argv[1])
1496 except:
1497 _log.exception('cannot open [%s] in OOo' % sys.argv[1])
1498 raise
1499
1500 class myCloseListener(unohelper.Base, oooXCloseListener):
1501 def disposing(self, evt):
1502 print "disposing:"
1503 def notifyClosing(self, evt):
1504 print "notifyClosing:"
1505 def queryClosing(self, evt, owner):
1506 # owner is True/False whether I am the owner of the doc
1507 print "queryClosing:"
1508
1509 l = myCloseListener()
1510 doc.addCloseListener(l)
1511
1512 tfs = doc.getTextFields().createEnumeration()
1513 print tfs
1514 print dir(tfs)
1515 while tfs.hasMoreElements():
1516 tf = tfs.nextElement()
1517 if tf.supportsService('com.sun.star.text.TextField.JumpEdit'):
1518 print tf.getPropertyValue('PlaceHolder')
1519 print " ", tf.getPropertyValue('Hint')
1520
1521 # doc.close(True) # closes but leaves open the dedicated OOo window
1522 doc.dispose() # closes and disposes of the OOo window
1523 #--------------------------------------------------------
1525 pat = gmPersonSearch.ask_for_patient()
1526 if pat is None:
1527 return
1528 gmPerson.set_active_patient(patient = pat)
1529
1530 doc = cOOoLetter(template_file = sys.argv[2])
1531 doc.open_in_ooo()
1532 print doc
1533 doc.show()
1534 #doc.replace_placeholders()
1535 #doc.save_in_ooo('~/test_cOOoLetter.odt')
1536 doc = None
1537 # doc.close_in_ooo()
1538 raw_input('press <ENTER> to continue')
1539 #--------------------------------------------------------
1540 # other
1541 #--------------------------------------------------------
1543 template = cFormTemplate(aPK_obj = sys.argv[2])
1544 print template
1545 print template.export_to_file()
1546 #--------------------------------------------------------
1548 template = cFormTemplate(aPK_obj = sys.argv[2])
1549 template.update_template_from_file(filename = sys.argv[3])
1550 #--------------------------------------------------------
1552 pat = gmPersonSearch.ask_for_patient()
1553 if pat is None:
1554 return
1555 gmPerson.set_active_patient(patient = pat)
1556
1557 gmPerson.gmCurrentProvider(provider = gmPerson.cStaff())
1558
1559 path = os.path.abspath(sys.argv[2])
1560 form = cLaTeXForm(template_file = path)
1561
1562 from Gnumed.wxpython import gmMacro
1563 ph = gmMacro.gmPlaceholderHandler()
1564 ph.debug = True
1565 instance_file = form.substitute_placeholders(data_source = ph)
1566 pdf_name = form.generate_output(instance_file = instance_file)
1567 print "final PDF file is:", pdf_name
1568 #--------------------------------------------------------
1570 pat = gmPersonSearch.ask_for_patient()
1571 if pat is None:
1572 return
1573 gmPerson.set_active_patient(patient = pat)
1574
1575 gmPerson.gmCurrentProvider(provider = gmPerson.cStaff())
1576
1577 path = os.path.abspath(sys.argv[2])
1578 form = cLaPDFForm(template_file = path)
1579
1580 from Gnumed.wxpython import gmMacro
1581 ph = gmMacro.gmPlaceholderHandler()
1582 ph.debug = True
1583 instance_file = form.substitute_placeholders(data_source = ph)
1584 pdf_name = form.generate_output(instance_file = instance_file)
1585 print "final PDF file is:", pdf_name
1586 #--------------------------------------------------------
1587 #--------------------------------------------------------
1588 # now run the tests
1589 #test_au()
1590 #test_de()
1591
1592 # OOo
1593 #test_init_ooo()
1594 #test_ooo_connect()
1595 #test_open_ooo_doc_from_srv()
1596 #test_open_ooo_doc_from_letter()
1597 #play_with_ooo()
1598 #test_cOOoLetter()
1599
1600 #test_cFormTemplate()
1601 #set_template_from_file()
1602 #test_latex_form()
1603 test_pdf_form()
1604
1605 #============================================================
1606
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Oct 18 04:00:26 2011 | http://epydoc.sourceforge.net |