| Home | Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf-8 -*-
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 __author__ ="Ian Haywood <ihaywood@gnu.org>, karsten.hilbert@gmx.net"
10
11
12 import os
13 import sys
14 import time
15 import os.path
16 import logging
17 import re as regex
18 import shutil
19 import random
20 import platform
21 import subprocess
22 import io
23 import codecs
24 import socket # needed for OOo on Windows
25 #, libxml2, libxslt
26 import shlex
27
28
29 if __name__ == '__main__':
30 sys.path.insert(0, '../../')
31 from Gnumed.pycommon import gmI18N
32 gmI18N.activate_locale()
33 gmI18N.install_domain(domain = 'gnumed')
34 from Gnumed.pycommon import gmTools
35 from Gnumed.pycommon import gmDispatcher
36 from Gnumed.pycommon import gmExceptions
37 from Gnumed.pycommon import gmMatchProvider
38 from Gnumed.pycommon import gmBorg
39 from Gnumed.pycommon import gmLog2
40 from Gnumed.pycommon import gmMimeLib
41 from Gnumed.pycommon import gmShellAPI
42 from Gnumed.pycommon import gmCfg
43 from Gnumed.pycommon import gmCfg2
44 from Gnumed.pycommon import gmBusinessDBObject
45 from Gnumed.pycommon import gmPG2
46 from Gnumed.pycommon import gmDateTime
47
48 from Gnumed.business import gmPerson
49 from Gnumed.business import gmStaff
50 from Gnumed.business import gmPersonSearch
51 from Gnumed.business import gmPraxis
52
53
54 _log = logging.getLogger('gm.forms')
55
56 #============================================================
57 # this order is also used in choice boxes for the engine
58 form_engine_abbrevs = ['O', 'L', 'I', 'G', 'P', 'A', 'X', 'T']
59
60 form_engine_names = {
61 'O': 'OpenOffice',
62 'L': 'LaTeX',
63 'I': 'Image editor',
64 'G': 'Gnuplot script',
65 'P': 'PDF forms',
66 'A': 'AbiWord',
67 'X': 'Xe(La)TeX',
68 'T': 'text export'
69 }
70
71 form_engine_template_wildcards = {
72 'O': '*.o?t',
73 'L': '*.tex',
74 'G': '*.gpl',
75 'P': '*.pdf',
76 'A': '*.abw',
77 'X': '*.tex',
78 'T': '*.ini'
79 }
80
81 # is filled in further below after each engine is defined
82 form_engines = {}
83
84 #============================================================
85 # match providers
86 #============================================================
88
90
91 query = """
92 SELECT
93 name_long AS data,
94 name_long AS list_label,
95 name_long AS field_label
96 FROM ref.v_paperwork_templates
97 WHERE name_long %(fragment_condition)s
98 ORDER BY list_label
99 """
100 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
101 #============================================================
103
105
106 query = """
107 SELECT
108 name_short AS data,
109 name_short AS list_label,
110 name_short AS field_label
111 FROM ref.v_paperwork_templates
112 WHERE name_short %(fragment_condition)s
113 ORDER BY name_short
114 """
115 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
116 #============================================================
118
120
121 query = """
122 SELECT DISTINCT ON (list_label)
123 pk AS data,
124 _(name) || ' (' || name || ')' AS list_label,
125 _(name) AS field_label
126 FROM ref.form_types
127 WHERE
128 _(name) %(fragment_condition)s
129 OR
130 name %(fragment_condition)s
131 ORDER BY list_label
132 """
133 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
134
135 #============================================================
137
138 _cmd_fetch_payload = 'SELECT * FROM ref.v_paperwork_templates WHERE pk_paperwork_template = %s'
139
140 _cmds_store_payload = [
141 """UPDATE ref.paperwork_templates SET
142 name_short = %(name_short)s,
143 name_long = %(name_long)s,
144 fk_template_type = %(pk_template_type)s,
145 instance_type = %(instance_type)s,
146 engine = %(engine)s,
147 in_use = %(in_use)s,
148 edit_after_substitution = %(edit_after_substitution)s,
149 filename = %(filename)s,
150 external_version = %(external_version)s
151 WHERE
152 pk = %(pk_paperwork_template)s
153 AND
154 xmin = %(xmin_paperwork_template)s
155 RETURNING
156 xmin AS xmin_paperwork_template
157 """
158 ]
159 _updatable_fields = [
160 'name_short',
161 'name_long',
162 'external_version',
163 'pk_template_type',
164 'instance_type',
165 'engine',
166 'in_use',
167 'filename',
168 'edit_after_substitution'
169 ]
170
171 _suffix4engine = {
172 'O': '.ott',
173 'L': '.tex',
174 'T': '.txt',
175 'X': '.xslt',
176 'I': '.img',
177 'P': '.pdf'
178 }
179
180 #--------------------------------------------------------
182 """The template itself better not be arbitrarily large unless you can handle that.
183
184 Note that the data type returned will be a buffer."""
185
186 cmd = 'SELECT data FROM ref.paperwork_templates WHERE pk = %(pk)s'
187 rows, idx = gmPG2.run_ro_queries (queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}], get_col_idx = False)
188
189 if len(rows) == 0:
190 raise gmExceptions.NoSuchBusinessObjectError('cannot retrieve data for template pk = %s' % self.pk_obj)
191
192 return rows[0][0]
193
194 template_data = property(_get_template_data, lambda x:x)
195
196 #--------------------------------------------------------
198 """Export form template from database into file."""
199
200 if filename is None:
201 if self._payload[self._idx['filename']] is None:
202 suffix = self.__class__._suffix4engine[self._payload[self._idx['engine']]]
203 else:
204 suffix = os.path.splitext(self._payload[self._idx['filename']].strip())[1].strip()
205 if suffix in ['', '.']:
206 suffix = self.__class__._suffix4engine[self._payload[self._idx['engine']]]
207
208 filename = gmTools.get_unique_filename (
209 prefix = 'gm-%s-Template-' % self._payload[self._idx['engine']],
210 suffix = suffix
211 )
212
213 data_query = {
214 'cmd': 'SELECT substring(data from %(start)s for %(size)s) FROM ref.paperwork_templates WHERE pk = %(pk)s',
215 'args': {'pk': self.pk_obj}
216 }
217
218 data_size_query = {
219 'cmd': 'select octet_length(data) from ref.paperwork_templates where pk = %(pk)s',
220 'args': {'pk': self.pk_obj}
221 }
222
223 result = gmPG2.bytea2file (
224 data_query = data_query,
225 filename = filename,
226 data_size_query = data_size_query,
227 chunk_size = chunksize
228 )
229 if result is False:
230 return None
231
232 return filename
233
234 #--------------------------------------------------------
236 gmPG2.file2bytea (
237 filename = filename,
238 query = 'update ref.paperwork_templates set data = %(data)s::bytea where pk = %(pk)s and xmin = %(xmin)s',
239 args = {'pk': self.pk_obj, 'xmin': self._payload[self._idx['xmin_paperwork_template']]}
240 )
241 # adjust for xmin change
242 self.refetch_payload()
243
244 #--------------------------------------------------------
246 fname = self.save_to_file()
247 engine = form_engines[self._payload[self._idx['engine']]]
248 form = engine(template_file = fname)
249 form.template = self
250 return form
251
252 #============================================================
254 cmd = 'select pk from ref.paperwork_templates where name_long = %(lname)s and external_version = %(ver)s'
255 args = {'lname': name_long, 'ver': external_version}
256 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
257
258 if len(rows) == 0:
259 _log.error('cannot load form template [%s - %s]', name_long, external_version)
260 return None
261
262 return cFormTemplate(aPK_obj = rows[0]['pk'])
263
264 #------------------------------------------------------------
265 -def get_form_templates(engine=None, active_only=False, template_types=None, excluded_types=None):
266 """Load form templates."""
267
268 args = {'eng': engine, 'in_use': active_only}
269 where_parts = ['1 = 1']
270
271 if engine is not None:
272 where_parts.append('engine = %(eng)s')
273
274 if active_only:
275 where_parts.append('in_use IS true')
276
277 if template_types is not None:
278 args['incl_types'] = tuple(template_types)
279 where_parts.append('template_type IN %(incl_types)s')
280
281 if excluded_types is not None:
282 args['excl_types'] = tuple(excluded_types)
283 where_parts.append('template_type NOT IN %(excl_types)s')
284
285 cmd = "SELECT * FROM ref.v_paperwork_templates WHERE %s ORDER BY in_use desc, name_long" % '\nAND '.join(where_parts)
286
287 rows, idx = gmPG2.run_ro_queries (
288 queries = [{'cmd': cmd, 'args': args}],
289 get_col_idx = True
290 )
291 templates = [ cFormTemplate(row = {'pk_field': 'pk_paperwork_template', 'data': r, 'idx': idx}) for r in rows ]
292
293 return templates
294
295 #------------------------------------------------------------
297 cmd = """
298 INSERT INTO ref.paperwork_templates (
299 fk_template_type,
300 name_short,
301 name_long,
302 external_version
303 ) VALUES (
304 %(type)s,
305 %(nshort)s,
306 %(nlong)s,
307 %(ext_version)s
308 )
309 RETURNING pk
310 """
311 args = {'type': template_type, 'nshort': name_short, 'nlong': name_long, 'ext_version': 'new'}
312 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
313 template = cFormTemplate(aPK_obj = rows[0][0])
314 return template
315
316 #------------------------------------------------------------
318 rows, idx = gmPG2.run_rw_queries (
319 queries = [{
320 'cmd': 'DELETE FROM ref.paperwork_templates WHERE pk = %(pk)s',
321 'args': {'pk': template['pk_paperwork_template']}
322 }]
323 )
324 return True
325
326 #============================================================
327 # OpenOffice/LibreOffice API
328 #============================================================
329 uno = None
330 cOOoDocumentCloseListener = None
331 writer_binary = None
332
333 # http://forum.openoffice.org/en/forum/viewtopic.php?t=36370
334 # http://stackoverflow.com/questions/4270962/using-pyuno-with-my-existing-python-installation
335
336 #-----------------------------------------------------------
338
339 try:
340 which = subprocess.Popen (
341 args = ('which', 'soffice'),
342 stdout = subprocess.PIPE,
343 stdin = subprocess.PIPE,
344 stderr = subprocess.PIPE,
345 universal_newlines = True
346 )
347 except (OSError, ValueError, subprocess.CalledProcessError):
348 _log.exception('there was a problem executing [which soffice]')
349 return
350
351 soffice_path, err = which.communicate()
352 soffice_path = soffice_path.strip('\n')
353 uno_path = os.path.abspath ( os.path.join (
354 os.path.dirname(os.path.realpath(soffice_path)),
355 '..',
356 'basis-link',
357 'program'
358 ))
359
360 _log.info('UNO should be at [%s], appending to sys.path', uno_path)
361
362 sys.path.append(uno_path)
363
364 #-----------------------------------------------------------
366 """FIXME: consider this:
367
368 try:
369 import uno
370 except:
371 print "This Script needs to be run with the python from OpenOffice.org"
372 print "Example: /opt/OpenOffice.org/program/python %s" % (
373 os.path.basename(sys.argv[0]))
374 print "Or you need to insert the right path at the top, where uno.py is."
375 print "Default: %s" % default_path
376 """
377 global uno
378 if uno is not None:
379 return
380
381 try:
382 import uno
383 except ImportError:
384 __configure_path_to_UNO()
385 import uno
386
387 global unohelper, oooXCloseListener, oooNoConnectException, oooPropertyValue
388
389 import unohelper
390 from com.sun.star.util import XCloseListener as oooXCloseListener
391 from com.sun.star.connection import NoConnectException as oooNoConnectException
392 from com.sun.star.beans import PropertyValue as oooPropertyValue
393
394 #----------------------------------
395 class _cOOoDocumentCloseListener(unohelper.Base, oooXCloseListener):
396 """Listens for events sent by OOo during the document closing
397 sequence and notifies the GNUmed client GUI so it can
398 import the closed document into the database.
399 """
400 def __init__(self, document=None):
401 self.document = document
402
403 def queryClosing(self, evt, owner):
404 # owner is True/False whether I am the owner of the doc
405 pass
406
407 def notifyClosing(self, evt):
408 pass
409
410 def disposing(self, evt):
411 self.document.on_disposed_by_ooo()
412 self.document = None
413 #----------------------------------
414
415 global cOOoDocumentCloseListener
416 cOOoDocumentCloseListener = _cOOoDocumentCloseListener
417
418 # search for writer binary
419 global writer_binary
420 found, binary = gmShellAPI.find_first_binary(binaries = [
421 'lowriter',
422 'oowriter',
423 'swriter'
424 ])
425 if found:
426 _log.debug('OOo/LO writer binary found: %s', binary)
427 writer_binary = binary
428 else:
429 _log.debug('OOo/LO writer binary NOT found')
430 raise ImportError('LibreOffice/OpenOffice (lowriter/oowriter/swriter) not found')
431
432 _log.debug('python UNO bridge successfully initialized')
433
434 #------------------------------------------------------------
436 """This class handles the connection to OOo.
437
438 Its Singleton instance stays around once initialized.
439 """
440 # FIXME: need to detect closure of OOo !
442
443 init_ooo()
444
445 self.__setup_connection_string()
446
447 self.resolver_uri = "com.sun.star.bridge.UnoUrlResolver"
448 self.desktop_uri = "com.sun.star.frame.Desktop"
449
450 self.max_connect_attempts = 5
451
452 self.local_context = uno.getComponentContext()
453 self.uri_resolver = self.local_context.ServiceManager.createInstanceWithContext(self.resolver_uri, self.local_context)
454
455 self.__desktop = None
456 #--------------------------------------------------------
457 # external API
458 #--------------------------------------------------------
460 if self.__desktop is None:
461 _log.debug('no desktop, no cleanup')
462 return
463
464 try:
465 self.__desktop.terminate()
466 except:
467 _log.exception('cannot terminate OOo desktop')
468 #--------------------------------------------------------
470 """<filename> must be absolute"""
471 if self.desktop is None:
472 _log.error('cannot access OOo desktop')
473 return None
474
475 filename = os.path.expanduser(filename)
476 filename = os.path.abspath(filename)
477 document_uri = uno.systemPathToFileUrl(filename)
478
479 _log.debug('%s -> %s', filename, document_uri)
480
481 doc = self.desktop.loadComponentFromURL(document_uri, "_blank", 0, ())
482 return doc
483 #--------------------------------------------------------
484 # internal helpers
485 #--------------------------------------------------------
487 # later factor this out !
488 dbcfg = gmCfg.cCfgSQL()
489 self.ooo_startup_settle_time = dbcfg.get2 (
490 option = 'external.ooo.startup_settle_time',
491 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
492 bias = 'workplace',
493 default = 3.0
494 )
495 #--------------------------------------------------------
497
498 # socket:
499 # ooo_port = u'2002'
500 # #self.ooo_start_cmd = 'oowriter -invisible -norestore -nofirststartwizard -nologo -accept="socket,host=localhost,port=%s;urp;StarOffice.ServiceManager"' % ooo_port
501 # self.ooo_start_cmd = 'oowriter -invisible -norestore -accept="socket,host=localhost,port=%s;urp;"' % ooo_port
502 # self.remote_context_uri = "uno:socket,host=localhost,port=%s;urp;StarOffice.ComponentContext" % ooo_port
503
504 # pipe:
505 pipe_name = "uno-gm2lo-%s" % str(random.random())[2:]
506 _log.debug('expecting OOo/LO server on named pipe [%s]', pipe_name)
507 self.ooo_start_cmd = '%s --invisible --norestore --accept="pipe,name=%s;urp" &' % (
508 writer_binary,
509 pipe_name
510 )
511 _log.debug('startup command: %s', self.ooo_start_cmd)
512
513 self.remote_context_uri = "uno:pipe,name=%s;urp;StarOffice.ComponentContext" % pipe_name
514 _log.debug('remote context URI: %s', self.remote_context_uri)
515 #--------------------------------------------------------
517 _log.info('trying to start OOo server')
518 _log.debug('startup command: %s', self.ooo_start_cmd)
519 os.system(self.ooo_start_cmd)
520 self.__get_startup_settle_time()
521 _log.debug('waiting %s seconds for OOo to start up', self.ooo_startup_settle_time)
522 time.sleep(self.ooo_startup_settle_time)
523 #--------------------------------------------------------
524 # properties
525 #--------------------------------------------------------
527 if self.__desktop is not None:
528 return self.__desktop
529
530 self.remote_context = None
531
532 attempts = self.max_connect_attempts
533 while attempts > 0:
534
535 _log.debug('attempt %s/%s', self.max_connect_attempts - attempts + 1, self.max_connect_attempts)
536
537 try:
538 self.remote_context = self.uri_resolver.resolve(self.remote_context_uri)
539 break
540 except oooNoConnectException:
541 _log.exception('cannot connect to OOo')
542
543 # first loop ?
544 if attempts == self.max_connect_attempts:
545 self.__startup_ooo()
546 else:
547 time.sleep(1)
548
549 attempts = attempts - 1
550
551 if self.remote_context is None:
552 raise OSError(-1, 'cannot connect to OpenOffice', self.remote_context_uri)
553
554 _log.debug('connection seems established')
555 self.__desktop = self.remote_context.ServiceManager.createInstanceWithContext(self.desktop_uri, self.remote_context)
556 _log.debug('got OOo desktop handle')
557 return self.__desktop
558
559 desktop = property(_get_desktop, lambda x:x)
560
561 #------------------------------------------------------------
563
565
566 self.template_file = template_file
567 self.instance_type = instance_type
568 self.ooo_doc = None
569 #--------------------------------------------------------
570 # external API
571 #--------------------------------------------------------
573 # connect to OOo
574 ooo_srv = gmOOoConnector()
575
576 # open doc in OOo
577 self.ooo_doc = ooo_srv.open_document(filename = self.template_file)
578 if self.ooo_doc is None:
579 _log.error('cannot open document in OOo')
580 return False
581
582 # listen for close events
583 pat = gmPerson.gmCurrentPatient()
584 pat.locked = True
585 listener = cOOoDocumentCloseListener(document = self)
586 self.ooo_doc.addCloseListener(listener)
587
588 return True
589 #--------------------------------------------------------
592 #--------------------------------------------------------
594
595 # new style embedded, implicit placeholders
596 searcher = self.ooo_doc.createSearchDescriptor()
597 searcher.SearchCaseSensitive = False
598 searcher.SearchRegularExpression = True
599 searcher.SearchWords = True
600 searcher.SearchString = handler.placeholder_regex
601
602 placeholder_instance = self.ooo_doc.findFirst(searcher)
603 while placeholder_instance is not None:
604 try:
605 val = handler[placeholder_instance.String]
606 except:
607 val = _('error with placeholder [%s]') % placeholder_instance.String
608 _log.exception(val)
609
610 if val is None:
611 val = _('error with placeholder [%s]') % placeholder_instance.String
612
613 placeholder_instance.String = val
614 placeholder_instance = self.ooo_doc.findNext(placeholder_instance.End, searcher)
615
616 if not old_style_too:
617 return
618
619 # old style "explicit" placeholders
620 text_fields = self.ooo_doc.getTextFields().createEnumeration()
621 while text_fields.hasMoreElements():
622 text_field = text_fields.nextElement()
623
624 # placeholder ?
625 if not text_field.supportsService('com.sun.star.text.TextField.JumpEdit'):
626 continue
627 # placeholder of type text ?
628 if text_field.PlaceHolderType != 0:
629 continue
630
631 replacement = handler[text_field.PlaceHolder]
632 if replacement is None:
633 continue
634
635 text_field.Anchor.setString(replacement)
636 #--------------------------------------------------------
638 if filename is not None:
639 target_url = uno.systemPathToFileUrl(os.path.abspath(os.path.expanduser(filename)))
640 save_args = (
641 oooPropertyValue('Overwrite', 0, True, 0),
642 oooPropertyValue('FormatFilter', 0, 'swriter: StarOffice XML (Writer)', 0)
643
644 )
645 # "store AS url" stores the doc, marks it unmodified and updates
646 # the internal media descriptor - as opposed to "store TO url"
647 self.ooo_doc.storeAsURL(target_url, save_args)
648 else:
649 self.ooo_doc.store()
650 #--------------------------------------------------------
652 self.ooo_doc.dispose()
653 pat = gmPerson.gmCurrentPatient()
654 pat.locked = False
655 self.ooo_doc = None
656 #--------------------------------------------------------
658 # get current file name from OOo, user may have used Save As
659 filename = uno.fileUrlToSystemPath(self.ooo_doc.URL)
660 # tell UI to import the file
661 gmDispatcher.send (
662 signal = 'import_document_from_file',
663 filename = filename,
664 document_type = self.instance_type,
665 unlock_patient = True,
666 pk_org_unit = gmPraxis.gmCurrentPraxisBranch()['pk_org_unit']
667 )
668 self.ooo_doc = None
669 #--------------------------------------------------------
670 # internal helpers
671 #--------------------------------------------------------
672
673 #============================================================
675 """Ancestor for forms."""
676
678 self.template = None
679 self.template_filename = template_file
680 _log.debug('working on template file [%s]', self.template_filename)
681 #--------------------------------------------------------
683 """Parse the template into an instance and replace placeholders with values."""
684 raise NotImplementedError
685 #--------------------------------------------------------
689 #--------------------------------------------------------
693 #--------------------------------------------------------
694 #--------------------------------------------------------
695 # def process(self, data_source=None):
696 # """Merge values into the form template.
697 # """
698 # pass
699 # #--------------------------------------------------------
700 # def cleanup(self):
701 # """
702 # A sop to TeX which can't act as a true filter: to delete temporary files
703 # """
704 # pass
705 # #--------------------------------------------------------
706 # def exe(self, command):
707 # """
708 # Executes the provided command.
709 # If command cotains %F. it is substituted with the filename
710 # Otherwise, the file is fed in on stdin
711 # """
712 # pass
713 # #--------------------------------------------------------
714 # def store(self, params=None):
715 # """Stores the parameters in the backend.
716 #
717 # - link_obj can be a cursor, a connection or a service name
718 # - assigning a cursor to link_obj allows the calling code to
719 # group the call to store() into an enclosing transaction
720 # (for an example see gmReferral.send_referral()...)
721 # """
722 # # some forms may not have values ...
723 # if params is None:
724 # params = {}
725 # patient_clinical = self.patient.emr
726 # encounter = patient_clinical.active_encounter['pk_encounter']
727 # # FIXME: get_active_episode is no more
728 # #episode = patient_clinical.get_active_episode()['pk_episode']
729 # # generate "forever unique" name
730 # cmd = "select name_short || ': <' || name_long || '::' || external_version || '>' from paperwork_templates where pk=%s";
731 # rows = gmPG.run_ro_query('reference', cmd, None, self.pk_def)
732 # form_name = None
733 # if rows is None:
734 # _log.error('error retrieving form def for [%s]' % self.pk_def)
735 # elif len(rows) == 0:
736 # _log.error('no form def for [%s]' % self.pk_def)
737 # else:
738 # form_name = rows[0][0]
739 # # we didn't get a name but want to store the form anyhow
740 # if form_name is None:
741 # form_name=time.time() # hopefully unique enough
742 # # in one transaction
743 # queries = []
744 # # - store form instance in form_instance
745 # cmd = "insert into form_instances(fk_form_def, form_name, fk_episode, fk_encounter) values (%s, %s, %s, %s)"
746 # queries.append((cmd, [self.pk_def, form_name, episode, encounter]))
747 # # - store params in form_data
748 # for key in params.keys():
749 # cmd = """
750 # insert into form_data(fk_instance, place_holder, value)
751 # values ((select currval('form_instances_pk_seq')), %s, %s::text)
752 # """
753 # queries.append((cmd, [key, params[key]]))
754 # # - get inserted PK
755 # queries.append(("select currval ('form_instances_pk_seq')", []))
756 # status, err = gmPG.run_commit('historica', queries, True)
757 # if status is None:
758 # _log.error('failed to store form [%s] (%s): %s' % (self.pk_def, form_name, err))
759 # return None
760 # return status
761
762 #================================================================
763 # OOo template forms
764 #----------------------------------------------------------------
766 """A forms engine wrapping OOo."""
767
769 super(self.__class__, self).__init__(template_file = template_file)
770
771 path, ext = os.path.splitext(self.template_filename)
772 if ext in [r'', r'.']:
773 ext = r'.odt'
774 self.instance_filename = r'%s-instance%s' % (path, ext)
775
776 #================================================================
777 # AbiWord template forms
778 #----------------------------------------------------------------
780 """A forms engine wrapping AbiWord."""
781
782 placeholder_regex = r'\$<.+?>\$'
783
785
786 super(cAbiWordForm, self).__init__(template_file = template_file)
787
788 # detect abiword
789 found, self.abiword_binary = gmShellAPI.detect_external_binary(binary = r'abiword')
790 if not found:
791 raise ImportError('<abiword(.exe)> not found')
792 #--------------------------------------------------------
794 # should *actually* properly parse the XML
795
796 path, ext = os.path.splitext(self.template_filename)
797 if ext in [r'', r'.']:
798 ext = r'.abw'
799 self.instance_filename = r'%s-instance%s' % (path, ext)
800
801 template_file = io.open(self.template_filename, mode = 'rt', encoding = 'utf8')
802 instance_file = io.open(self.instance_filename, mode = 'wt', encoding = 'utf8')
803
804 if self.template is not None:
805 # inject placeholder values
806 data_source.set_placeholder('form_name_long', self.template['name_long'])
807 data_source.set_placeholder('form_name_short', self.template['name_short'])
808 data_source.set_placeholder('form_version', self.template['external_version'])
809 data_source.set_placeholder('form_version_internal', gmTools.coalesce(self.template['gnumed_revision'], '', '%s'))
810 data_source.set_placeholder('form_last_modified', gmDateTime.pydt_strftime(self.template['last_modified'], '%Y-%b-%d %H:%M'))
811
812 data_source.escape_style = 'xml'
813 data_source.escape_function = None # gmTools.xml_escape_text() ?
814
815 for line in template_file:
816
817 if line.strip() in ['', '\r', '\n', '\r\n']:
818 instance_file.write(line)
819 continue
820
821 # 1) find placeholders in this line
822 placeholders_in_line = regex.findall(cAbiWordForm.placeholder_regex, line, regex.IGNORECASE)
823 # 2) and replace them
824 for placeholder in placeholders_in_line:
825 try:
826 val = data_source[placeholder.replace('<', '<').replace('>', '>')]
827 except:
828 val = _('error with placeholder [%s]') % gmTools.xml_escape_string(placeholder)
829 _log.exception(val)
830
831 if val is None:
832 val = _('error with placeholder [%s]') % gmTools.xml_escape_string(placeholder)
833
834 line = line.replace(placeholder, val)
835
836 instance_file.write(line)
837
838 instance_file.close()
839 template_file.close()
840
841 if self.template is not None:
842 # remove temporary placeholders
843 data_source.unset_placeholder('form_name_long')
844 data_source.unset_placeholder('form_name_short')
845 data_source.unset_placeholder('form_version')
846 data_source.unset_placeholder('form_version_internal')
847 data_source.unset_placeholder('form_last_modified')
848
849 return
850 #--------------------------------------------------------
852 enc = sys.getfilesystemencoding()
853 cmd = (r'%s %s' % (self.abiword_binary, self.instance_filename.encode(enc))).encode(enc)
854 result = gmShellAPI.run_command_in_shell(command = cmd, blocking = True)
855 self.re_editable_filenames = [self.instance_filename]
856 return result
857 #--------------------------------------------------------
859
860 if instance_file is None:
861 instance_file = self.instance_filename
862 try:
863 open(instance_file, 'r').close()
864 except:
865 _log.exception('cannot access form instance file [%s]', instance_file)
866 gmLog2.log_stack_trace()
867 return None
868 self.instance_filename = instance_file
869
870 _log.debug('ignoring <format> directive [%s], generating PDF', format)
871
872 pdf_name = os.path.splitext(self.instance_filename)[0] + '.pdf'
873 cmd = '%s --to=pdf --to-name=%s %s' % (
874 self.abiword_binary,
875 pdf_name,
876 self.instance_filename
877 )
878 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
879 _log.error('problem running abiword, cannot generate form output')
880 gmDispatcher.send(signal = 'statustext', msg = _('Error running AbiWord. Cannot generate PDF.'), beep = True)
881 return None
882
883 self.final_output_filenames = [pdf_name]
884 return pdf_name
885
886 #----------------------------------------------------------------
887 form_engines['A'] = cAbiWordForm
888
889 #================================================================
890 # text template forms
891 #----------------------------------------------------------------
893 """A forms engine outputting data as text for further processing."""
894
896
897 super(self.__class__, self).__init__(template_file = template_file)
898
899 # create sandbox to play in (and don't assume much
900 # of anything about the template_file except that it
901 # is at our disposal for reading)
902 self.__sandbox_dir = gmTools.mk_sandbox_dir()
903 _log.debug('sandbox directory: [%s]', self.__sandbox_dir)
904
905 # parse template file which is an INI style config
906 # file containing the actual template plus metadata
907 self.form_definition_filename = self.template_filename
908 _log.debug('form definition file: [%s]', self.form_definition_filename)
909 cfg_file = io.open(self.form_definition_filename, mode = 'rt', encoding = 'utf8')
910 self.form_definition = gmCfg2.parse_INI_stream(stream = cfg_file)
911 cfg_file.close()
912
913 # extract actual template into a file
914 template_text = self.form_definition['form::template']
915 if isinstance(template_text, type([])):
916 template_text = '\n'.join(self.form_definition['form::template'])
917 self.template_filename = gmTools.get_unique_filename (
918 prefix = 'gm-',
919 suffix = '.txt',
920 tmp_dir = self.__sandbox_dir
921 )
922 _log.debug('template file: [%s]', self.template_filename)
923 f = io.open(self.template_filename, mode = 'wt', encoding = 'utf8')
924 f.write(template_text)
925 f.close()
926
927 #--------------------------------------------------------
929
930 if self.template is not None:
931 # inject placeholder values
932 data_source.set_placeholder('form_name_long', self.template['name_long'])
933 data_source.set_placeholder('form_name_short', self.template['name_short'])
934 data_source.set_placeholder('form_version', self.template['external_version'])
935 data_source.set_placeholder('form_version_internal', gmTools.coalesce(self.template['gnumed_revision'], '', '%s'))
936 data_source.set_placeholder('form_last_modified', gmDateTime.pydt_strftime(self.template['last_modified'], '%Y-%b-%d %H:%M'))
937
938 base = os.path.join(self.__sandbox_dir, gmTools.fname_stem(self.template_filename))
939 filenames = [
940 self.template_filename,
941 r'%s-result-pass-1.txt' % base,
942 r'%s-result-pass-2.txt' % base,
943 r'%s-result-pass-3.txt' % base
944 ]
945 regexen = [
946 'dummy',
947 data_source.first_pass_placeholder_regex,
948 data_source.second_pass_placeholder_regex,
949 data_source.third_pass_placeholder_regex
950 ]
951
952 current_pass = 1
953 while current_pass < 4:
954 _log.debug('placeholder substitution pass #%s', current_pass)
955 found_placeholders = self.__substitute_placeholders (
956 input_filename = filenames[current_pass-1],
957 output_filename = filenames[current_pass],
958 data_source = data_source,
959 placeholder_regex = regexen[current_pass]
960 )
961 current_pass += 1
962
963 # remove temporary placeholders
964 data_source.unset_placeholder('form_name_long')
965 data_source.unset_placeholder('form_name_short')
966 data_source.unset_placeholder('form_version')
967 data_source.unset_placeholder('form_version_internal')
968 data_source.unset_placeholder('form_last_modified')
969
970 self.instance_filename = self.re_editable_filenames[0]
971
972 return True
973
974 #--------------------------------------------------------
975 - def __substitute_placeholders(self, data_source=None, input_filename=None, output_filename=None, placeholder_regex=None):
976
977 _log.debug('[%s] -> [%s]', input_filename, output_filename)
978 _log.debug('searching for placeholders with pattern: %s', placeholder_regex)
979
980 template_file = io.open(input_filename, mode = 'rt', encoding = 'utf8')
981 instance_file = io.open(output_filename, mode = 'wt', encoding = 'utf8')
982
983 for line in template_file:
984 # empty lines
985 if line.strip() in ['', '\r', '\n', '\r\n']:
986 instance_file.write(line)
987 continue
988
989 # 1) find placeholders in this line
990 placeholders_in_line = regex.findall(placeholder_regex, line, regex.IGNORECASE)
991 if len(placeholders_in_line) == 0:
992 instance_file.write(line)
993 continue
994
995 # 2) replace them
996 _log.debug('%s placeholders found in this line', len(placeholders_in_line))
997 for placeholder in placeholders_in_line:
998 try:
999 val = data_source[placeholder]
1000 except:
1001 val = _('error with placeholder [%s]') % placeholder
1002 _log.exception(val)
1003 if val is None:
1004 val = _('error with placeholder [%s]') % placeholder
1005
1006 line = line.replace(placeholder, val)
1007
1008 instance_file.write(line)
1009
1010 instance_file.close()
1011 self.re_editable_filenames = [output_filename]
1012 template_file.close()
1013
1014 #--------------------------------------------------------
1016
1017 editor_cmd = None
1018 try:
1019 editor_cmd = self.form_definition['form::editor'] % self.instance_filename
1020 except KeyError:
1021 _log.debug('no explicit editor defined for text template')
1022
1023 if editor_cmd is None:
1024 mimetype = 'text/plain'
1025 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename)
1026 if editor_cmd is None:
1027 # also consider text *viewers* since pretty much any of them will be an editor as well
1028 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename)
1029
1030 if editor_cmd is not None:
1031 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True)
1032 self.re_editable_filenames = [self.instance_filename]
1033
1034 return result
1035
1036 #--------------------------------------------------------
1038 try:
1039 post_processor = self.form_definition['form::post processor'] % {
1040 'input_name': self.instance_filename,
1041 'output_name': self.instance_filename + '.output'
1042 }
1043 except KeyError:
1044 _log.debug('no explicit post processor defined for text template')
1045 return True
1046
1047 self.final_output_filenames = [self.instance_filename + '.output']
1048
1049 return gmShellAPI.run_command_in_shell(command = post_processor, blocking = True)
1050 #------------------------------------------------------------
1051 form_engines['T'] = cTextForm
1052
1053 #================================================================
1054 # LaTeX template forms
1055 #----------------------------------------------------------------
1057 """A forms engine wrapping LaTeX."""
1058
1060
1061 # create sandbox for LaTeX to play in (and don't assume
1062 # much of anything about the template_file except that it
1063 # is at our disposal for reading)
1064 sandbox_dir = gmTools.mk_sandbox_dir(prefix = gmTools.fname_stem(template_file) + '_')
1065 _log.debug('LaTeX sandbox directory: [%s]', sandbox_dir)
1066 shutil.copy(template_file, sandbox_dir)
1067 template_file = os.path.join(sandbox_dir, os.path.split(template_file)[1])
1068
1069 super(self.__class__, self).__init__(template_file = template_file)
1070
1071 self.__sandbox_dir = sandbox_dir
1072
1073 #--------------------------------------------------------
1075
1076 if self.template is not None:
1077 # inject placeholder values
1078 data_source.set_placeholder('form_name_long', self.template['name_long'])
1079 data_source.set_placeholder('form_name_short', self.template['name_short'])
1080 data_source.set_placeholder('form_version', self.template['external_version'])
1081 data_source.set_placeholder('form_version_internal', gmTools.coalesce(self.template['gnumed_revision'], '', '%s'))
1082 data_source.set_placeholder('form_last_modified', gmDateTime.pydt_strftime(self.template['last_modified'], '%Y-%b-%d %H:%M'))
1083
1084 data_source.escape_function = gmTools.tex_escape_string
1085 data_source.escape_style = 'latex'
1086
1087 path, ext = os.path.splitext(self.template_filename)
1088 if ext in [r'', r'.']:
1089 ext = r'.tex'
1090
1091 filenames = [
1092 self.template_filename,
1093 r'%s-result-pass-1%s' % (path, ext),
1094 r'%s-result-pass-2%s' % (path, ext),
1095 r'%s-result-pass-3%s' % (path, ext)
1096 ]
1097 regexen = [
1098 'dummy',
1099 data_source.first_pass_placeholder_regex,
1100 data_source.second_pass_placeholder_regex,
1101 data_source.third_pass_placeholder_regex
1102 ]
1103
1104 current_pass = 1
1105 while current_pass < 4:
1106 _log.debug('placeholder substitution pass #%s', current_pass)
1107 found_placeholders = self.__substitute_placeholders (
1108 input_filename = filenames[current_pass-1],
1109 output_filename = filenames[current_pass],
1110 data_source = data_source,
1111 placeholder_regex = regexen[current_pass]
1112 )
1113 current_pass += 1
1114
1115 # remove temporary placeholders
1116 data_source.unset_placeholder('form_name_long')
1117 data_source.unset_placeholder('form_name_short')
1118 data_source.unset_placeholder('form_version')
1119 data_source.unset_placeholder('form_version_internal')
1120 data_source.unset_placeholder('form_last_modified')
1121
1122 self.instance_filename = self.re_editable_filenames[0]
1123
1124 return
1125 #--------------------------------------------------------
1126 - def __substitute_placeholders(self, data_source=None, input_filename=None, output_filename=None, placeholder_regex=None):
1127
1128 _log.debug('[%s] -> [%s]', input_filename, output_filename)
1129 _log.debug('searching for placeholders with pattern: %s', placeholder_regex)
1130
1131 template_file = io.open(input_filename, mode = 'rt', encoding = 'utf8')
1132 instance_file = io.open(output_filename, mode = 'wt', encoding = 'utf8')
1133
1134 for line in template_file:
1135 # empty lines
1136 if line.strip() in ['', '\r', '\n', '\r\n']:
1137 instance_file.write(line)
1138 continue
1139 # TeX-comment-only lines
1140 if line.lstrip().startswith('%'):
1141 instance_file.write(line)
1142 continue
1143
1144 # 1) find placeholders in this line
1145 placeholders_in_line = regex.findall(placeholder_regex, line, regex.IGNORECASE)
1146 if len(placeholders_in_line) == 0:
1147 instance_file.write(line)
1148 continue
1149
1150 # 2) replace them
1151 _log.debug('%s placeholders found in this line', len(placeholders_in_line))
1152 for placeholder in placeholders_in_line:
1153 try:
1154 val = data_source[placeholder]
1155 except:
1156 _log.exception('error with placeholder [%s]', placeholder)
1157 val = gmTools.tex_escape_string(_('error with placeholder [%s]') % placeholder)
1158 if val is None:
1159 _log.debug('error with placeholder [%s]', placeholder)
1160 val = gmTools.tex_escape_string(_('error with placeholder [%s]') % placeholder)
1161 line = line.replace(placeholder, val)
1162 instance_file.write(line)
1163
1164 instance_file.close()
1165 self.re_editable_filenames = [output_filename]
1166 template_file.close()
1167
1168 return
1169
1170 #--------------------------------------------------------
1172
1173 mimetypes = [
1174 'application/x-latex',
1175 'application/x-tex',
1176 'text/latex',
1177 'text/tex',
1178 'text/plain'
1179 ]
1180
1181 for mimetype in mimetypes:
1182 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename)
1183 if editor_cmd is not None:
1184 break
1185
1186 if editor_cmd is None:
1187 # LaTeX code is text: also consider text *viewers*
1188 # since pretty much any of them will be an editor as well
1189 for mimetype in mimetypes:
1190 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename)
1191 if editor_cmd is not None:
1192 break
1193
1194 if editor_cmd is None:
1195 return False
1196
1197 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True)
1198 self.re_editable_filenames = [self.instance_filename]
1199 return result
1200 #--------------------------------------------------------
1202
1203 if instance_file is None:
1204 instance_file = self.instance_filename
1205
1206 try:
1207 open(instance_file, 'r').close()
1208 except:
1209 _log.exception('cannot access form instance file [%s]', instance_file)
1210 gmLog2.log_stack_trace()
1211 return None
1212
1213 self.instance_filename = instance_file
1214
1215 _log.debug('ignoring <format> directive [%s], generating PDF', format)
1216
1217 # LaTeX can need up to three runs to get cross references et al right
1218 if platform.system() == 'Windows':
1219 draft_cmd = r'pdflatex.exe -draftmode -recorder -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1220 final_cmd = r'pdflatex.exe -recorder -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1221 else:
1222 draft_cmd = r'pdflatex -draftmode -recorder -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1223 final_cmd = r'pdflatex -recorder -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1224 for run_cmd in [draft_cmd, draft_cmd, final_cmd]:
1225 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]):
1226 _log.error('problem running pdflatex, cannot generate form output')
1227 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdflatex. Cannot turn LaTeX template into PDF.'), beep = True)
1228 return None
1229
1230 sandboxed_pdf_name = '%s.pdf' % os.path.splitext(self.instance_filename)[0]
1231 target_dir = os.path.normpath(os.path.join(os.path.split(sandboxed_pdf_name)[0], '..'))
1232 final_pdf_name = os.path.join (
1233 target_dir,
1234 os.path.split(sandboxed_pdf_name)[1]
1235 )
1236 _log.debug('copying sandboxed PDF: %s -> %s', sandboxed_pdf_name, final_pdf_name)
1237 try:
1238 shutil.copy2(sandboxed_pdf_name, target_dir)
1239 except IOError:
1240 _log.exception('cannot open/move sandboxed PDF')
1241 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True)
1242 return None
1243
1244 self.final_output_filenames = [final_pdf_name]
1245
1246 return final_pdf_name
1247 #------------------------------------------------------------
1248 form_engines['L'] = cLaTeXForm
1249
1250 #================================================================
1251 # Xe(La)TeX template forms
1252 #----------------------------------------------------------------
1253 # Xe(La)TeX: http://www.scholarsfonts.net/xetextt.pdf
1255 """A forms engine wrapping Xe(La)TeX."""
1256
1258
1259 # create sandbox for LaTeX to play in (and don't assume
1260 # much of anything about the template_file except that it
1261 # is at our disposal)
1262 sandbox_dir = gmTools.mk_sandbox_dir(prefix = gmTools.fname_stem(template_file) + '_')
1263 _log.debug('Xe(La)TeX sandbox directory: [%s]', sandbox_dir)
1264 shutil.copy(template_file, sandbox_dir)
1265 template_file = os.path.join(sandbox_dir, os.path.split(template_file)[1])
1266
1267 super(self.__class__, self).__init__(template_file = template_file)
1268
1269 self.__sandbox_dir = sandbox_dir
1270 #--------------------------------------------------------
1272
1273 if self.template is not None:
1274 # inject placeholder values
1275 data_source.set_placeholder('form_name_long', self.template['name_long'])
1276 data_source.set_placeholder('form_name_short', self.template['name_short'])
1277 data_source.set_placeholder('form_version', self.template['external_version'])
1278 data_source.set_placeholder('form_version_internal', gmTools.coalesce(self.template['gnumed_revision'], '', '%s'))
1279 data_source.set_placeholder('form_last_modified', gmDateTime.pydt_strftime(self.template['last_modified'], '%Y-%b-%d %H:%M'))
1280
1281 data_source.escape_function = gmTools.xetex_escape_string
1282 data_source.escape_style = 'xetex'
1283
1284 path, ext = os.path.splitext(self.template_filename)
1285 if ext in [r'', r'.']:
1286 ext = r'.tex'
1287
1288 filenames = [
1289 self.template_filename,
1290 r'%s-result_run1%s' % (path, ext),
1291 r'%s-result_run2%s' % (path, ext),
1292 r'%s-result_run3%s' % (path, ext)
1293 ]
1294
1295 found_placeholders = True
1296 current_run = 1
1297 while found_placeholders and (current_run < 4):
1298 _log.debug('placeholder substitution run #%s', current_run)
1299 found_placeholders = self.__substitute_placeholders (
1300 input_filename = filenames[current_run-1],
1301 output_filename = filenames[current_run],
1302 data_source = data_source
1303 )
1304 current_run += 1
1305
1306 if self.template is not None:
1307 # remove temporary placeholders
1308 data_source.unset_placeholder('form_name_long')
1309 data_source.unset_placeholder('form_name_short')
1310 data_source.unset_placeholder('form_version')
1311 data_source.unset_placeholder('form_version_internal')
1312 data_source.unset_placeholder('form_last_modified')
1313
1314 self.instance_filename = self.re_editable_filenames[0]
1315
1316 return
1317 #--------------------------------------------------------
1318 - def __substitute_placeholders(self, data_source=None, input_filename=None, output_filename=None):
1319 _log.debug('[%s] -> [%s]', input_filename, output_filename)
1320
1321 found_placeholders = False
1322
1323 template_file = io.open(input_filename, mode = 'rt', encoding = 'utf8')
1324 instance_file = io.open(output_filename, mode = 'wt', encoding = 'utf8')
1325
1326 for line in template_file:
1327
1328 if line.strip() in ['', '\r', '\n', '\r\n']: # empty lines
1329 instance_file.write(line)
1330 continue
1331 if line.startswith('%'): # TeX comment
1332 instance_file.write(line)
1333 continue
1334
1335 for placeholder_regex in [data_source.first_pass_placeholder_regex, data_source.second_pass_placeholder_regex, data_source.third_pass_placeholder_regex]:
1336 # 1) find placeholders in this line
1337 placeholders_in_line = regex.findall(placeholder_regex, line, regex.IGNORECASE)
1338 if len(placeholders_in_line) == 0:
1339 continue
1340 _log.debug('%s placeholders found with pattern: %s', len(placeholders_in_line), placeholder_regex)
1341 found_placeholders = True
1342 # 2) replace them
1343 for placeholder in placeholders_in_line:
1344 try:
1345 val = data_source[placeholder]
1346 except:
1347 _log.exception('error with placeholder [%s]', placeholder)
1348 val = gmTools.tex_escape_string(_('error with placeholder [%s]') % placeholder)
1349
1350 if val is None:
1351 _log.debug('error with placeholder [%s]', placeholder)
1352 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder)
1353
1354 line = line.replace(placeholder, val)
1355
1356 instance_file.write(line)
1357
1358 instance_file.close()
1359 self.re_editable_filenames = [output_filename]
1360 template_file.close()
1361
1362 return found_placeholders
1363 #--------------------------------------------------------
1365
1366 mimetypes = [
1367 'application/x-xetex',
1368 'application/x-latex',
1369 'application/x-tex',
1370 'text/plain'
1371 ]
1372
1373 for mimetype in mimetypes:
1374 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename)
1375 if editor_cmd is not None:
1376 break
1377
1378 if editor_cmd is None:
1379 # Xe(La)TeX code is utf8: also consider text *viewers*
1380 # since pretty much any of them will be an editor as well
1381 for mimetype in mimetypes:
1382 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename)
1383 if editor_cmd is not None:
1384 break
1385
1386 if editor_cmd is None:
1387 return False
1388
1389 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True)
1390 self.re_editable_filenames = [self.instance_filename]
1391 return result
1392 #--------------------------------------------------------
1394
1395 if instance_file is None:
1396 instance_file = self.instance_filename
1397
1398 try:
1399 open(instance_file, 'r').close()
1400 except:
1401 _log.exception('cannot access form instance file [%s]', instance_file)
1402 gmLog2.log_stack_trace()
1403 return None
1404
1405 self.instance_filename = instance_file
1406
1407 _log.debug('ignoring <format> directive [%s], generating PDF', format)
1408
1409 # Xe(La)TeX can need up to three runs to get cross references et al right
1410 if platform.system() == 'Windows':
1411 # not yet supported: -draftmode
1412 # does not support: -shell-escape
1413 draft_cmd = r'xelatex.exe -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1414 final_cmd = r'xelatex.exe -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1415 else:
1416 # not yet supported: -draftmode
1417 draft_cmd = r'xelatex -interaction=nonstopmode -output-directory=%s -shell-escape %s' % (self.__sandbox_dir, self.instance_filename)
1418 final_cmd = r'xelatex -interaction=nonstopmode -output-directory=%s -shell-escape %s' % (self.__sandbox_dir, self.instance_filename)
1419
1420 for run_cmd in [draft_cmd, draft_cmd, final_cmd]:
1421 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]):
1422 _log.error('problem running xelatex, cannot generate form output')
1423 gmDispatcher.send(signal = 'statustext', msg = _('Error running xelatex. Cannot turn Xe(La)TeX template into PDF.'), beep = True)
1424 return None
1425
1426 sandboxed_pdf_name = '%s.pdf' % os.path.splitext(self.instance_filename)[0]
1427 target_dir = os.path.normpath(os.path.join(os.path.split(sandboxed_pdf_name)[0], '..'))
1428 final_pdf_name = os.path.join (
1429 target_dir,
1430 os.path.split(sandboxed_pdf_name)[1]
1431 )
1432 _log.debug('copying sandboxed PDF: %s -> %s', sandboxed_pdf_name, final_pdf_name)
1433 try:
1434 shutil.copy2(sandboxed_pdf_name, target_dir)
1435 except IOError:
1436 _log.exception('cannot open/move sandboxed PDF')
1437 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True)
1438 return None
1439
1440 self.final_output_filenames = [final_pdf_name]
1441
1442 return final_pdf_name
1443
1444 #------------------------------------------------------------
1445 form_engines['X'] = cXeTeXForm
1446
1447 #============================================================
1448 # Gnuplot template forms
1449 #------------------------------------------------------------
1451 """A forms engine wrapping Gnuplot."""
1452
1453 #--------------------------------------------------------
1457 #--------------------------------------------------------
1459 """Allow editing the instance of the template."""
1460 self.re_editable_filenames = []
1461 return True
1462 #--------------------------------------------------------
1464 """Generate output suitable for further processing outside this class, e.g. printing.
1465
1466 Expects .data_filename to be set.
1467 """
1468 self.conf_filename = gmTools.get_unique_filename(prefix = 'gm2gpl-', suffix = '.conf')
1469 conf_file = io.open(self.conf_filename, mode = 'wt', encoding = 'utf8')
1470 conf_file.write('# setting the gnuplot data file\n')
1471 conf_file.write("gm2gpl_datafile = '%s'\n" % self.data_filename)
1472 conf_file.close()
1473
1474 # FIXME: cater for configurable path
1475 if platform.system() == 'Windows':
1476 exec_name = 'gnuplot.exe'
1477 else:
1478 exec_name = 'gnuplot'
1479
1480 args = [
1481 exec_name,
1482 '-p', # let plot window persist after main gnuplot process exits
1483 self.conf_filename, # contains the gm2gpl_datafile setting which, in turn, contains the actual data
1484 self.template_filename # contains the plotting instructions (IOW a user provided gnuplot script)
1485 ]
1486 _log.debug('plotting args: %s' % str(args))
1487
1488 try:
1489 gp = subprocess.Popen (
1490 args = args,
1491 close_fds = True
1492 )
1493 except (OSError, ValueError, subprocess.CalledProcessError):
1494 _log.exception('there was a problem executing gnuplot')
1495 gmDispatcher.send(signal = 'statustext', msg = _('Error running gnuplot. Cannot plot data.'), beep = True)
1496 return
1497
1498 gp.communicate()
1499
1500 self.final_output_filenames = [
1501 self.conf_filename,
1502 self.data_filename,
1503 self.template_filename
1504 ]
1505
1506 return
1507 #------------------------------------------------------------
1508 form_engines['G'] = cGnuplotForm
1509
1510 #============================================================
1511 # fPDF form engine
1512 #------------------------------------------------------------
1514 """A forms engine wrapping PDF forms.
1515
1516 Johann Felix Soden <johfel@gmx.de> helped with this.
1517
1518 http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf
1519
1520 http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/fdf_data_exchange.pdf
1521 """
1522
1524
1525 super(cPDFForm, self).__init__(template_file = template_file)
1526
1527 # detect pdftk
1528 found, self.pdftk_binary = gmShellAPI.detect_external_binary(binary = r'pdftk')
1529 if not found:
1530 raise ImportError('<pdftk(.exe)> not found')
1531 return # should be superfluous, actually
1532
1533 enc = sys.getfilesystemencoding()
1534 self.pdftk_binary = self.pdftk_binary.encode(enc)
1535
1536 base_name, ext = os.path.splitext(self.template_filename)
1537 self.fdf_dumped_filename = ('%s.fdf' % base_name).encode(enc)
1538 self.fdf_replaced_filename = ('%s-replaced.fdf' % base_name).encode(enc)
1539 self.pdf_filled_filename = ('%s-filled.pdf' % base_name).encode(enc)
1540 self.pdf_flattened_filename = ('%s-filled-flattened.pdf' % base_name).encode(enc)
1541 #--------------------------------------------------------
1543
1544 # dump form fields from template
1545 cmd_line = [
1546 self.pdftk_binary,
1547 self.template_filename,
1548 r'generate_fdf',
1549 r'output',
1550 self.fdf_dumped_filename
1551 ]
1552 _log.debug(' '.join(cmd_line))
1553 try:
1554 pdftk = subprocess.Popen(cmd_line)
1555 except OSError:
1556 _log.exception('cannot run <pdftk> (dump data from form)')
1557 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdftk. Cannot extract fields from PDF form template.'), beep = True)
1558 return False
1559
1560 pdftk.communicate()
1561 if pdftk.returncode != 0:
1562 _log.error('<pdftk> returned [%s], failed to dump data from PDF form into FDF', pdftk.returncode)
1563 return False
1564
1565 # parse dumped FDF file for "/V (...)" records
1566 # and replace placeholders therein
1567 fdf_dumped_file = io.open(self.fdf_dumped_filename, mode = 'rt', encoding = 'utf8')
1568 fdf_replaced_file = io.open(self.fdf_replaced_filename, mode = 'wt', encoding = 'utf8')
1569
1570 string_value_regex = r'\s*/V\s*\(.+\)\s*$'
1571 for line in fdf_dumped_file:
1572 if not regex.match(string_value_regex, line):
1573 fdf_replaced_file.write(line)
1574 continue
1575
1576 # strip cruft around the string value
1577 raw_str_val = line.strip() # remove framing whitespace
1578 raw_str_val = raw_str_val[2:] # remove leading "/V"
1579 raw_str_val = raw_str_val.lstrip() # remove whitespace between "/V" and "("
1580 raw_str_val = raw_str_val[1:] # remove opening "("
1581 raw_str_val = raw_str_val[2:] # remove BOM-16-BE
1582 raw_str_val = raw_str_val.rstrip() # remove trailing whitespace
1583 raw_str_val = raw_str_val[:-1] # remove closing ")"
1584
1585 # work on FDF escapes
1586 raw_str_val = raw_str_val.replace('\(', '(') # remove escaping of "("
1587 raw_str_val = raw_str_val.replace('\)', ')') # remove escaping of ")"
1588
1589 # by now raw_str_val should contain the actual
1590 # string value, albeit encoded as UTF-16, so
1591 # decode it into a unicode object,
1592 # split multi-line fields on "\n" literal
1593 raw_str_lines = raw_str_val.split('\x00\\n')
1594 value_template_lines = []
1595 for raw_str_line in raw_str_lines:
1596 value_template_lines.append(raw_str_line.decode('utf_16_be'))
1597
1598 replaced_lines = []
1599 for value_template in value_template_lines:
1600 # find any placeholders within
1601 placeholders_in_value = regex.findall(data_source.placeholder_regex, value_template, regex.IGNORECASE)
1602 for placeholder in placeholders_in_value:
1603 try:
1604 replacement = data_source[placeholder]
1605 except:
1606 _log.exception(replacement)
1607 replacement = _('error with placeholder [%s]') % placeholder
1608 if replacement is None:
1609 replacement = _('error with placeholder [%s]') % placeholder
1610 value_template = value_template.replace(placeholder, replacement)
1611
1612 value_template = value_template.encode('utf_16_be')
1613
1614 if len(placeholders_in_value) > 0:
1615 value_template = value_template.replace(r'(', r'\(')
1616 value_template = value_template.replace(r')', r'\)')
1617
1618 replaced_lines.append(value_template)
1619
1620 replaced_line = '\x00\\n'.join(replaced_lines)
1621
1622 fdf_replaced_file.write('/V (')
1623 fdf_replaced_file.write(codecs.BOM_UTF16_BE)
1624 fdf_replaced_file.write(replaced_line)
1625 fdf_replaced_file.write(')\n')
1626
1627 fdf_replaced_file.close()
1628 fdf_dumped_file.close()
1629
1630 # merge replaced data back into form
1631 cmd_line = [
1632 self.pdftk_binary,
1633 self.template_filename,
1634 r'fill_form',
1635 self.fdf_replaced_filename,
1636 r'output',
1637 self.pdf_filled_filename
1638 ]
1639 _log.debug(' '.join(cmd_line))
1640 try:
1641 pdftk = subprocess.Popen(cmd_line)
1642 except OSError:
1643 _log.exception('cannot run <pdftk> (merge data into form)')
1644 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdftk. Cannot fill in PDF form template.'), beep = True)
1645 return False
1646
1647 pdftk.communicate()
1648 if pdftk.returncode != 0:
1649 _log.error('<pdftk> returned [%s], failed to merge FDF data into PDF form', pdftk.returncode)
1650 return False
1651
1652 return True
1653 #--------------------------------------------------------
1655 mimetypes = [
1656 'application/pdf',
1657 'application/x-pdf'
1658 ]
1659
1660 for mimetype in mimetypes:
1661 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.pdf_filled_filename)
1662 if editor_cmd is not None:
1663 break
1664
1665 if editor_cmd is None:
1666 _log.debug('editor cmd not found, trying viewer cmd')
1667 for mimetype in mimetypes:
1668 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.pdf_filled_filename)
1669 if editor_cmd is not None:
1670 break
1671
1672 if editor_cmd is None:
1673 return False
1674
1675 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True)
1676
1677 path, fname = os.path.split(self.pdf_filled_filename)
1678 candidate = os.path.join(gmTools.gmPaths().home_dir, fname)
1679
1680 if os.access(candidate, os.R_OK):
1681 _log.debug('filled-in PDF found: %s', candidate)
1682 os.rename(self.pdf_filled_filename, self.pdf_filled_filename + '.bak')
1683 shutil.move(candidate, path)
1684 else:
1685 _log.debug('filled-in PDF not found: %s', candidate)
1686
1687 self.re_editable_filenames = [self.pdf_filled_filename]
1688
1689 return result
1690 #--------------------------------------------------------
1692 """Generate output suitable for further processing outside this class, e.g. printing."""
1693
1694 # eventually flatten the filled in form so we
1695 # can keep both a flattened and an editable copy:
1696 cmd_line = [
1697 self.pdftk_binary,
1698 self.pdf_filled_filename,
1699 r'output',
1700 self.pdf_flattened_filename,
1701 r'flatten'
1702 ]
1703 _log.debug(' '.join(cmd_line))
1704 try:
1705 pdftk = subprocess.Popen(cmd_line)
1706 except OSError:
1707 _log.exception('cannot run <pdftk> (flatten filled in form)')
1708 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdftk. Cannot flatten filled in PDF form.'), beep = True)
1709 return None
1710
1711 pdftk.communicate()
1712 if pdftk.returncode != 0:
1713 _log.error('<pdftk> returned [%s], failed to flatten filled in PDF form', pdftk.returncode)
1714 return None
1715
1716 self.final_output_filenames = [self.pdf_flattened_filename]
1717
1718 return self.pdf_flattened_filename
1719 #------------------------------------------------------------
1720 form_engines['P'] = cPDFForm
1721
1722 #============================================================
1723 # older code
1724 #------------------------------------------------------------
1726 """A forms engine wrapping LaTeX.
1727 """
1731
1733 try:
1734 latex = Cheetah.Template.Template (self.template, filter=LaTeXFilter, searchList=[params])
1735 # create a 'sandbox' directory for LaTeX to play in
1736 self.tmp = tempfile.mktemp ()
1737 os.makedirs (self.tmp)
1738 self.oldcwd = os.getcwd()
1739 os.chdir (self.tmp)
1740 stdin = os.popen ("latex", "w", 2048)
1741 stdin.write (str (latex)) #send text. LaTeX spits it's output into stdout
1742 # FIXME: send LaTeX output to the logger
1743 stdin.close ()
1744 if not gmShellAPI.run_command_in_shell("dvips texput.dvi -o texput.ps", blocking=True):
1745 raise FormError ('DVIPS returned error')
1746 except EnvironmentError as e:
1747 _log.error(e.strerror)
1748 raise FormError (e.strerror)
1749 return open("texput.ps")
1750
1752 """
1753 For testing purposes, runs Xdvi on the intermediate TeX output
1754 WARNING: don't try this on Windows
1755 """
1756 gmShellAPI.run_command_in_shell("xdvi texput.dvi", blocking=True)
1757
1759 if "%F" in command:
1760 command.replace ("%F", "texput.ps")
1761 else:
1762 command = "%s < texput.ps" % command
1763 try:
1764 if not gmShellAPI.run_command_in_shell(command, blocking=True):
1765 _log.error("external command %s returned non-zero" % command)
1766 raise FormError ('external command %s returned error' % command)
1767 except EnvironmentError as e:
1768 _log.error(e.strerror)
1769 raise FormError (e.strerror)
1770 return True
1771
1773 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print')
1774 self.exe (command)
1775
1777 """
1778 Delete all the LaTeX output iles
1779 """
1780 for i in os.listdir ('.'):
1781 os.unlink (i)
1782 os.chdir (self.oldcwd)
1783 os.rmdir (self.tmp)
1784
1785
1786
1787
1788 #================================================================
1789 # define a class for HTML forms (for printing)
1790 #================================================================
1792 """This class can create XML document from requested data,
1793 then process it with XSLT template and display results
1794 """
1795
1796 # FIXME: make the path configurable ?
1797 _preview_program = 'oowriter ' #this program must be in the system PATH
1798
1800
1801 if template is None:
1802 raise ValueError('%s: cannot create form instance without a template' % __name__)
1803
1804 cFormEngine.__init__(self, template = template)
1805
1806 self._FormData = None
1807
1808 # here we know/can assume that the template was stored as a utf-8
1809 # encoded string so use that conversion to create unicode:
1810 #self._XSLTData = str(str(template.template_data), 'UTF-8')
1811 # but in fact, str() knows how to handle buffers, so simply:
1812 self._XSLTData = str(self.template.template_data, 'UTF-8', 'strict')
1813
1814 # we must still devise a method of extracting the SQL query:
1815 # - either by retrieving it from a particular tag in the XSLT or
1816 # - by making the stored template actually be a dict which, unpickled,
1817 # has the keys "xslt" and "sql"
1818 self._SQL_query = 'select 1' #this sql query must output valid xml
1819 #--------------------------------------------------------
1820 # external API
1821 #--------------------------------------------------------
1823 """get data from backend and process it with XSLT template to produce readable output"""
1824
1825 # extract SQL (this is wrong but displays what is intended)
1826 xslt = libxml2.parseDoc(self._XSLTData)
1827 root = xslt.children
1828 for child in root:
1829 if child.type == 'element':
1830 self._SQL_query = child.content
1831 break
1832
1833 # retrieve data from backend
1834 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': self._SQL_query, 'args': sql_parameters}], get_col_idx = False)
1835
1836 __header = '<?xml version="1.0" encoding="UTF-8"?>\n'
1837 __body = rows[0][0]
1838
1839 # process XML data according to supplied XSLT, producing HTML
1840 self._XMLData =__header + __body
1841 style = libxslt.parseStylesheetDoc(xslt)
1842 xml = libxml2.parseDoc(self._XMLData)
1843 html = style.applyStylesheet(xml, None)
1844 self._FormData = html.serialize()
1845
1846 style.freeStylesheet()
1847 xml.freeDoc()
1848 html.freeDoc()
1849 #--------------------------------------------------------
1851 if self._FormData is None:
1852 raise ValueError('Preview request for empty form. Make sure the form is properly initialized and process() was performed')
1853
1854 fname = gmTools.get_unique_filename(prefix = 'gm_XSLT_form-', suffix = '.html')
1855 #html_file = os.open(fname, 'wb')
1856 #html_file.write(self._FormData.encode('UTF-8'))
1857 html_file = io.open(fname, mode = 'wt', encoding = 'utf8', errors = 'strict') # or 'replace' ?
1858 html_file.write(self._FormData)
1859 html_file.close()
1860
1861 cmd = '%s %s' % (self.__class__._preview_program, fname)
1862
1863 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = False):
1864 _log.error('%s: cannot launch report preview program' % __name__)
1865 return False
1866
1867 #os.unlink(self.filename) #delete file
1868 #FIXME: under Windows the temp file is deleted before preview program gets it (under Linux it works OK)
1869
1870 return True
1871 #--------------------------------------------------------
1875
1876
1877 #=====================================================
1878 #class LaTeXFilter(Cheetah.Filters.Filter):
1881 """
1882 Convience function to escape ISO-Latin-1 strings for TeX output
1883 WARNING: not all ISO-Latin-1 characters are expressible in TeX
1884 FIXME: nevertheless, there are a few more we could support
1885
1886 Also intelligently convert lists and tuples into TeX-style table lines
1887 """
1888 if type(item) is str:
1889 item = item.replace ("\\", "\\backslash") # I wonder about this, do we want users to be able to use raw TeX?
1890 item = item.replace ("&", "\\&")
1891 item = item.replace ("$", "\\$")
1892 item = item.replace ('"', "") # okay, that's not right, but easiest solution for now
1893 item = item.replace ("\n", "\\\\ ")
1894 if len (item.strip ()) == 0:
1895 item = "\\relax " # sometimes TeX really hates empty strings, this seems to mollify it
1896 # FIXME: cover all of ISO-Latin-1 which can be expressed in TeX
1897 item = item.encode ('latin-1', 'replace')
1898 trans = {'ß':'\\ss{}', 'ä': '\\"{a}', 'Ä' :'\\"{A}', 'ö': '\\"{o}', 'Ö': '\\"{O}', 'ü': '\\"{u}', 'Ü': '\\"{U}',
1899 '\x8a':'\\v{S}', '\x8a':'\\OE{}', '\x9a':'\\v{s}', '\x9c': '\\oe{}', '\a9f':'\\"{Y}', #Microsloth extensions
1900 '\x86': '{\\dag}', '\x87': '{\\ddag}', '\xa7':'{\\S}', '\xb6': '{\\P}', '\xa9': '{\\copyright}', '\xbf': '?`',
1901 '\xc0':'\\`{A}', '\xa1': "\\'{A}", '\xa2': '\\^{A}', '\xa3':'\\~{A}', '\\xc5': '{\AA}',
1902 '\xc7':'\\c{C}', '\xc8':'\\`{E}',
1903 '\xa1': '!`',
1904 '\xb5':'$\mu$', '\xa3': '\pounds{}', '\xa2':'cent'
1905 }
1906 for k, i in trans.items ():
1907 item = item.replace (k, i)
1908 elif type(item) is list or type(item) is tuple:
1909 item = string.join ([self.conv_enc(i, ' & ') for i in item], table_sep)
1910 elif item is None:
1911 item = '\\relax % Python None\n'
1912 elif type(item) is int or type(item) is float:
1913 item = str(item)
1914 else:
1915 item = str(item)
1916 _log.warning("unknown type %s, string %s" % (type(item), item))
1917 return item
1918
1919
1920 #===========================================================
1923
1924 #============================================================
1925 # convenience functions
1926 #------------------------------------------------------------
1928 """
1929 Instantiates a FormEngine based on the form ID or name from the backend
1930 """
1931 try:
1932 # it's a number: match to form ID
1933 id = int (id)
1934 cmd = 'select template, engine, pk from paperwork_templates where pk = %s'
1935 except ValueError:
1936 # it's a string, match to the form's name
1937 # FIXME: can we somehow OR like this: where name_short=%s OR name_long=%s ?
1938 cmd = 'select template, engine, flags, pk from paperwork_templates where name_short = %s'
1939 result = gmPG.run_ro_query ('reference', cmd, None, id)
1940 if result is None:
1941 _log.error('error getting form [%s]' % id)
1942 raise gmExceptions.FormError ('error getting form [%s]' % id)
1943 if len(result) == 0:
1944 _log.error('no form [%s] found' % id)
1945 raise gmExceptions.FormError ('no such form found [%s]' % id)
1946 if result[0][1] == 'L':
1947 return LaTeXForm (result[0][2], result[0][0])
1948 elif result[0][1] == 'T':
1949 return TextForm (result[0][2], result[0][0])
1950 else:
1951 _log.error('no form engine [%s] for form [%s]' % (result[0][1], id))
1952 raise FormError ('no engine [%s] for form [%s]' % (result[0][1], id))
1953 #-------------------------------------------------------------
1960 #-------------------------------------------------------------
1961
1962 test_letter = """
1963 \\documentclass{letter}
1964 \\address{ $DOCTOR \\\\
1965 $DOCTORADDRESS}
1966 \\signature{$DOCTOR}
1967
1968 \\begin{document}
1969 \\begin{letter}{$RECIPIENTNAME \\\\
1970 $RECIPIENTADDRESS}
1971
1972 \\opening{Dear $RECIPIENTNAME}
1973
1974 \\textbf{Re:} $PATIENTNAME, DOB: $DOB, $PATIENTADDRESS \\\\
1975
1976 $TEXT
1977
1978 \\ifnum$INCLUDEMEDS>0
1979 \\textbf{Medications List}
1980
1981 \\begin{tabular}{lll}
1982 $MEDSLIST
1983 \\end{tabular}
1984 \\fi
1985
1986 \\ifnum$INCLUDEDISEASES>0
1987 \\textbf{Disease List}
1988
1989 \\begin{tabular}{l}
1990 $DISEASELIST
1991 \\end{tabular}
1992 \\fi
1993
1994 \\closing{$CLOSING}
1995
1996 \\end{letter}
1997 \\end{document}
1998 """
1999
2000
2002 f = io.open('../../test-area/ian/terry-form.tex')
2003 params = {
2004 'RECIPIENT': "Dr. R. Terry\n1 Main St\nNewcastle",
2005 'DOCTORSNAME': 'Ian Haywood',
2006 'DOCTORSADDRESS': '1 Smith St\nMelbourne',
2007 'PATIENTNAME':'Joe Bloggs',
2008 'PATIENTADDRESS':'18 Fred St\nMelbourne',
2009 'REQUEST':'echocardiogram',
2010 'THERAPY':'on warfarin',
2011 'CLINICALNOTES':"""heard new murmur
2012 Here's some
2013 crap to demonstrate how it can cover multiple lines.""",
2014 'COPYADDRESS':'Jack Jones\nHannover, Germany',
2015 'ROUTINE':1,
2016 'URGENT':0,
2017 'FAX':1,
2018 'PHONE':1,
2019 'PENSIONER':1,
2020 'VETERAN':0,
2021 'PADS':0,
2022 'INSTRUCTIONS':'Take the blue pill, Neo'
2023 }
2024 form = LaTeXForm (1, f.read())
2025 form.process (params)
2026 form.xdvi ()
2027 form.cleanup ()
2028
2030 form = LaTeXForm (2, test_letter)
2031 params = {'RECIPIENTNAME':'Dr. Richard Terry',
2032 'RECIPIENTADDRESS':'1 Main St\nNewcastle',
2033 'DOCTOR':'Dr. Ian Haywood',
2034 'DOCTORADDRESS':'1 Smith St\nMelbourne',
2035 'PATIENTNAME':'Joe Bloggs',
2036 'PATIENTADDRESS':'18 Fred St, Melbourne',
2037 'TEXT':"""This is the main text of the referral letter""",
2038 'DOB':'12/3/65',
2039 'INCLUDEMEDS':1,
2040 'MEDSLIST':[["Amoxycillin", "500mg", "TDS"], ["Perindopril", "4mg", "OD"]],
2041 'INCLUDEDISEASES':0, 'DISEASELIST':'',
2042 'CLOSING':'Yours sincerely,'
2043 }
2044 form.process (params)
2045 print(os.getcwd())
2046 form.xdvi()
2047 form.cleanup()
2048
2049 #------------------------------------------------------------
2051 template = io.open('../../test-area/ian/Formularkopf-DE.tex')
2052 form = LaTeXForm(template=template.read())
2053 params = {
2054 'PATIENT LASTNAME': 'Kirk',
2055 'PATIENT FIRSTNAME': 'James T.',
2056 'PATIENT STREET': 'Hauptstrasse',
2057 'PATIENT ZIP': '02999',
2058 'PATIENT TOWN': 'Gross Saerchen',
2059 'PATIENT DOB': '22.03.1931'
2060 }
2061 form.process(params)
2062 form.xdvi()
2063 form.cleanup()
2064
2065 #============================================================
2066 # main
2067 #------------------------------------------------------------
2068 if __name__ == '__main__':
2069
2070 if len(sys.argv) < 2:
2071 sys.exit()
2072
2073 if sys.argv[1] != 'test':
2074 sys.exit()
2075
2076 gmDateTime.init()
2077
2078 #--------------------------------------------------------
2079 # OOo
2080 #--------------------------------------------------------
2082 init_ooo()
2083 #--------------------------------------------------------
2088 #--------------------------------------------------------
2090 srv = gmOOoConnector()
2091 doc = srv.open_document(filename = sys.argv[2])
2092 print("document:", doc)
2093 #--------------------------------------------------------
2095 doc = cOOoLetter(template_file = sys.argv[2])
2096 doc.open_in_ooo()
2097 print("document:", doc)
2098 input('press <ENTER> to continue')
2099 doc.show()
2100 #doc.replace_placeholders()
2101 #doc.save_in_ooo('~/test_cOOoLetter.odt')
2102 # doc = None
2103 # doc.close_in_ooo()
2104 input('press <ENTER> to continue')
2105 #--------------------------------------------------------
2107 try:
2108 doc = open_uri_in_ooo(filename=sys.argv[1])
2109 except:
2110 _log.exception('cannot open [%s] in OOo' % sys.argv[1])
2111 raise
2112
2113 class myCloseListener(unohelper.Base, oooXCloseListener):
2114 def disposing(self, evt):
2115 print("disposing:")
2116 def notifyClosing(self, evt):
2117 print("notifyClosing:")
2118 def queryClosing(self, evt, owner):
2119 # owner is True/False whether I am the owner of the doc
2120 print("queryClosing:")
2121
2122 l = myCloseListener()
2123 doc.addCloseListener(l)
2124
2125 tfs = doc.getTextFields().createEnumeration()
2126 print(tfs)
2127 print(dir(tfs))
2128 while tfs.hasMoreElements():
2129 tf = tfs.nextElement()
2130 if tf.supportsService('com.sun.star.text.TextField.JumpEdit'):
2131 print(tf.getPropertyValue('PlaceHolder'))
2132 print(" ", tf.getPropertyValue('Hint'))
2133
2134 # doc.close(True) # closes but leaves open the dedicated OOo window
2135 doc.dispose() # closes and disposes of the OOo window
2136 #--------------------------------------------------------
2138 pat = gmPersonSearch.ask_for_patient()
2139 if pat is None:
2140 return
2141 gmPerson.set_active_patient(patient = pat)
2142
2143 doc = cOOoLetter(template_file = sys.argv[2])
2144 doc.open_in_ooo()
2145 print(doc)
2146 doc.show()
2147 #doc.replace_placeholders()
2148 #doc.save_in_ooo('~/test_cOOoLetter.odt')
2149 doc = None
2150 # doc.close_in_ooo()
2151 input('press <ENTER> to continue')
2152 #--------------------------------------------------------
2153 # other
2154 #--------------------------------------------------------
2156 template = cFormTemplate(aPK_obj = sys.argv[2])
2157 print(template)
2158 print(template.save_to_file())
2159 #--------------------------------------------------------
2161 template = cFormTemplate(aPK_obj = sys.argv[2])
2162 template.update_template_from_file(filename = sys.argv[3])
2163 #--------------------------------------------------------
2165 pat = gmPersonSearch.ask_for_patient()
2166 if pat is None:
2167 return
2168 gmPerson.set_active_patient(patient = pat)
2169
2170 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
2171
2172 path = os.path.abspath(sys.argv[2])
2173 form = cLaTeXForm(template_file = path)
2174
2175 from Gnumed.wxpython import gmMacro
2176 ph = gmMacro.gmPlaceholderHandler()
2177 ph.debug = True
2178 instance_file = form.substitute_placeholders(data_source = ph)
2179 pdf_name = form.generate_output(instance_file = instance_file)
2180 print("final PDF file is:", pdf_name)
2181 #--------------------------------------------------------
2183 pat = gmPersonSearch.ask_for_patient()
2184 if pat is None:
2185 return
2186 gmPerson.set_active_patient(patient = pat)
2187
2188 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
2189
2190 path = os.path.abspath(sys.argv[2])
2191 form = cPDFForm(template_file = path)
2192
2193 from Gnumed.wxpython import gmMacro
2194 ph = gmMacro.gmPlaceholderHandler()
2195 ph.debug = True
2196 instance_file = form.substitute_placeholders(data_source = ph)
2197 pdf_name = form.generate_output(instance_file = instance_file)
2198 print("final PDF file is:", pdf_name)
2199 #--------------------------------------------------------
2201 pat = gmPersonSearch.ask_for_patient()
2202 if pat is None:
2203 return
2204 gmPerson.set_active_patient(patient = pat)
2205
2206 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
2207
2208 path = os.path.abspath(sys.argv[2])
2209 form = cAbiWordForm(template_file = path)
2210
2211 from Gnumed.wxpython import gmMacro
2212 ph = gmMacro.gmPlaceholderHandler()
2213 ph.debug = True
2214 instance_file = form.substitute_placeholders(data_source = ph)
2215 form.edit()
2216 final_name = form.generate_output(instance_file = instance_file)
2217 print("final file is:", final_name)
2218 #--------------------------------------------------------
2220
2221 from Gnumed.business import gmPraxis
2222
2223 branches = gmPraxis.get_praxis_branches()
2224 praxis = gmPraxis.gmCurrentPraxisBranch(branches[0])
2225 print(praxis)
2226
2227 pat = gmPersonSearch.ask_for_patient()
2228 if pat is None:
2229 return
2230 gmPerson.set_active_patient(patient = pat)
2231
2232 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
2233
2234 path = os.path.abspath(sys.argv[2])
2235 form = cTextForm(template_file = path)
2236
2237 from Gnumed.wxpython import gmMacro
2238 ph = gmMacro.gmPlaceholderHandler()
2239 ph.debug = True
2240 print("placeholder substitution worked:", form.substitute_placeholders(data_source = ph))
2241 print(form.re_editable_filenames)
2242 form.edit()
2243 form.generate_output()
2244 #--------------------------------------------------------
2245 #--------------------------------------------------------
2246 #--------------------------------------------------------
2247 # now run the tests
2248 #test_au()
2249 #test_de()
2250
2251 # OOo
2252 #test_init_ooo()
2253 #test_ooo_connect()
2254 #test_open_ooo_doc_from_srv()
2255 #test_open_ooo_doc_from_letter()
2256 #play_with_ooo()
2257 #test_cOOoLetter()
2258
2259 #test_cFormTemplate()
2260 #set_template_from_file()
2261 #test_latex_form()
2262 #test_pdf_form()
2263 #test_abiword_form()
2264 test_text_form()
2265
2266 #============================================================
2267
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu May 10 01:55:20 2018 | http://epydoc.sourceforge.net |