| 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 # remove extra linefeeds which the docutils ReST2LaTeX
1076 # converter likes to add but which makes pdflatex go
1077 # crazy when ending up inside KOMAScript variables
1078 return gmTools.rst2latex_snippet(text).strip()
1079
1080 #--------------------------------------------------------
1082
1083 if self.template is not None:
1084 # inject placeholder values
1085 data_source.set_placeholder('form_name_long', self.template['name_long'])
1086 data_source.set_placeholder('form_name_short', self.template['name_short'])
1087 data_source.set_placeholder('form_version', self.template['external_version'])
1088 data_source.set_placeholder('form_version_internal', gmTools.coalesce(self.template['gnumed_revision'], '', '%s'))
1089 data_source.set_placeholder('form_last_modified', gmDateTime.pydt_strftime(self.template['last_modified'], '%Y-%b-%d %H:%M'))
1090
1091 data_source.escape_function = gmTools.tex_escape_string
1092 data_source.escape_style = 'latex'
1093
1094 path, ext = os.path.splitext(self.template_filename)
1095 if ext in [r'', r'.']:
1096 ext = r'.tex'
1097
1098 filenames = [
1099 self.template_filename,
1100 r'%s-result-pass-1%s' % (path, ext),
1101 r'%s-result-pass-2%s' % (path, ext),
1102 r'%s-result-pass-3%s' % (path, ext)
1103 ]
1104 regexen = [
1105 'dummy',
1106 data_source.first_pass_placeholder_regex,
1107 data_source.second_pass_placeholder_regex,
1108 data_source.third_pass_placeholder_regex
1109 ]
1110
1111 current_pass = 1
1112 while current_pass < 4:
1113 _log.debug('placeholder substitution pass #%s', current_pass)
1114 found_placeholders = self.__substitute_placeholders (
1115 input_filename = filenames[current_pass-1],
1116 output_filename = filenames[current_pass],
1117 data_source = data_source,
1118 placeholder_regex = regexen[current_pass]
1119 )
1120 current_pass += 1
1121
1122 # remove temporary placeholders
1123 data_source.unset_placeholder('form_name_long')
1124 data_source.unset_placeholder('form_name_short')
1125 data_source.unset_placeholder('form_version')
1126 data_source.unset_placeholder('form_version_internal')
1127 data_source.unset_placeholder('form_last_modified')
1128
1129 self.instance_filename = self.re_editable_filenames[0]
1130
1131 return
1132
1133 #--------------------------------------------------------
1134 - def __substitute_placeholders(self, data_source=None, input_filename=None, output_filename=None, placeholder_regex=None):
1135
1136 _log.debug('[%s] -> [%s]', input_filename, output_filename)
1137 _log.debug('searching for placeholders with pattern: %s', placeholder_regex)
1138
1139 template_file = io.open(input_filename, mode = 'rt', encoding = 'utf8')
1140 instance_file = io.open(output_filename, mode = 'wt', encoding = 'utf8')
1141
1142 for line in template_file:
1143 # empty lines
1144 if line.strip() in ['', '\r', '\n', '\r\n']:
1145 instance_file.write(line)
1146 continue
1147 # TeX-comment-only lines
1148 if line.lstrip().startswith('%'):
1149 instance_file.write(line)
1150 continue
1151
1152 # 1) find placeholders in this line
1153 placeholders_in_line = regex.findall(placeholder_regex, line, regex.IGNORECASE)
1154 if len(placeholders_in_line) == 0:
1155 instance_file.write(line)
1156 continue
1157
1158 # 2) replace them
1159 _log.debug('%s placeholders found in this line', len(placeholders_in_line))
1160 for placeholder in placeholders_in_line:
1161 if 'free_text' in placeholder:
1162 # enable reStructuredText processing
1163 data_source.escape_function = self._rst2latex_transform
1164 else:
1165 data_source.escape_function = gmTools.tex_escape_string
1166 try:
1167 val = data_source[placeholder]
1168 except:
1169 _log.exception('error with placeholder [%s]', placeholder)
1170 val = gmTools.tex_escape_string(_('error with placeholder [%s]') % placeholder)
1171 if val is None:
1172 _log.debug('error with placeholder [%s]', placeholder)
1173 val = gmTools.tex_escape_string(_('error with placeholder [%s]') % placeholder)
1174 line = line.replace(placeholder, val)
1175 instance_file.write(line)
1176
1177 instance_file.close()
1178 self.re_editable_filenames = [output_filename]
1179 template_file.close()
1180
1181 return
1182
1183 #--------------------------------------------------------
1185
1186 mimetypes = [
1187 'application/x-latex',
1188 'application/x-tex',
1189 'text/latex',
1190 'text/tex',
1191 'text/plain'
1192 ]
1193
1194 for mimetype in mimetypes:
1195 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename)
1196 if editor_cmd is not None:
1197 break
1198
1199 if editor_cmd is None:
1200 # LaTeX code is text: also consider text *viewers*
1201 # since pretty much any of them will be an editor as well
1202 for mimetype in mimetypes:
1203 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename)
1204 if editor_cmd is not None:
1205 break
1206
1207 if editor_cmd is None:
1208 return False
1209
1210 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True)
1211 self.re_editable_filenames = [self.instance_filename]
1212 return result
1213 #--------------------------------------------------------
1215
1216 if instance_file is None:
1217 instance_file = self.instance_filename
1218
1219 try:
1220 open(instance_file, 'r').close()
1221 except:
1222 _log.exception('cannot access form instance file [%s]', instance_file)
1223 gmLog2.log_stack_trace()
1224 return None
1225
1226 self.instance_filename = instance_file
1227
1228 _log.debug('ignoring <format> directive [%s], generating PDF', format)
1229
1230 # LaTeX can need up to three runs to get cross references et al right
1231 if platform.system() == 'Windows':
1232 draft_cmd = r'pdflatex.exe -draftmode -recorder -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1233 final_cmd = r'pdflatex.exe -recorder -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1234 else:
1235 draft_cmd = r'pdflatex -draftmode -recorder -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1236 final_cmd = r'pdflatex -recorder -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1237 for run_cmd in [draft_cmd, draft_cmd, final_cmd]:
1238 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]):
1239 _log.error('problem running pdflatex, cannot generate form output')
1240 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdflatex. Cannot turn LaTeX template into PDF.'), beep = True)
1241 return None
1242
1243 sandboxed_pdf_name = '%s.pdf' % os.path.splitext(self.instance_filename)[0]
1244 target_dir = os.path.normpath(os.path.join(os.path.split(sandboxed_pdf_name)[0], '..'))
1245 final_pdf_name = os.path.join (
1246 target_dir,
1247 os.path.split(sandboxed_pdf_name)[1]
1248 )
1249 _log.debug('copying sandboxed PDF: %s -> %s', sandboxed_pdf_name, final_pdf_name)
1250 try:
1251 shutil.copy2(sandboxed_pdf_name, target_dir)
1252 except IOError:
1253 _log.exception('cannot open/move sandboxed PDF')
1254 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True)
1255 return None
1256
1257 self.final_output_filenames = [final_pdf_name]
1258
1259 return final_pdf_name
1260 #------------------------------------------------------------
1261 form_engines['L'] = cLaTeXForm
1262
1263 #================================================================
1264 # Xe(La)TeX template forms
1265 #----------------------------------------------------------------
1266 # Xe(La)TeX: http://www.scholarsfonts.net/xetextt.pdf
1268 """A forms engine wrapping Xe(La)TeX."""
1269
1271
1272 # create sandbox for LaTeX to play in (and don't assume
1273 # much of anything about the template_file except that it
1274 # is at our disposal)
1275 sandbox_dir = gmTools.mk_sandbox_dir(prefix = gmTools.fname_stem(template_file) + '_')
1276 _log.debug('Xe(La)TeX sandbox directory: [%s]', sandbox_dir)
1277 shutil.copy(template_file, sandbox_dir)
1278 template_file = os.path.join(sandbox_dir, os.path.split(template_file)[1])
1279
1280 super(self.__class__, self).__init__(template_file = template_file)
1281
1282 self.__sandbox_dir = sandbox_dir
1283 #--------------------------------------------------------
1285
1286 if self.template is not None:
1287 # inject placeholder values
1288 data_source.set_placeholder('form_name_long', self.template['name_long'])
1289 data_source.set_placeholder('form_name_short', self.template['name_short'])
1290 data_source.set_placeholder('form_version', self.template['external_version'])
1291 data_source.set_placeholder('form_version_internal', gmTools.coalesce(self.template['gnumed_revision'], '', '%s'))
1292 data_source.set_placeholder('form_last_modified', gmDateTime.pydt_strftime(self.template['last_modified'], '%Y-%b-%d %H:%M'))
1293
1294 data_source.escape_function = gmTools.xetex_escape_string
1295 data_source.escape_style = 'xetex'
1296
1297 path, ext = os.path.splitext(self.template_filename)
1298 if ext in [r'', r'.']:
1299 ext = r'.tex'
1300
1301 filenames = [
1302 self.template_filename,
1303 r'%s-result_run1%s' % (path, ext),
1304 r'%s-result_run2%s' % (path, ext),
1305 r'%s-result_run3%s' % (path, ext)
1306 ]
1307
1308 found_placeholders = True
1309 current_run = 1
1310 while found_placeholders and (current_run < 4):
1311 _log.debug('placeholder substitution run #%s', current_run)
1312 found_placeholders = self.__substitute_placeholders (
1313 input_filename = filenames[current_run-1],
1314 output_filename = filenames[current_run],
1315 data_source = data_source
1316 )
1317 current_run += 1
1318
1319 if self.template is not None:
1320 # remove temporary placeholders
1321 data_source.unset_placeholder('form_name_long')
1322 data_source.unset_placeholder('form_name_short')
1323 data_source.unset_placeholder('form_version')
1324 data_source.unset_placeholder('form_version_internal')
1325 data_source.unset_placeholder('form_last_modified')
1326
1327 self.instance_filename = self.re_editable_filenames[0]
1328
1329 return
1330 #--------------------------------------------------------
1331 - def __substitute_placeholders(self, data_source=None, input_filename=None, output_filename=None):
1332 _log.debug('[%s] -> [%s]', input_filename, output_filename)
1333
1334 found_placeholders = False
1335
1336 template_file = io.open(input_filename, mode = 'rt', encoding = 'utf8')
1337 instance_file = io.open(output_filename, mode = 'wt', encoding = 'utf8')
1338
1339 for line in template_file:
1340
1341 if line.strip() in ['', '\r', '\n', '\r\n']: # empty lines
1342 instance_file.write(line)
1343 continue
1344 if line.startswith('%'): # TeX comment
1345 instance_file.write(line)
1346 continue
1347
1348 for placeholder_regex in [data_source.first_pass_placeholder_regex, data_source.second_pass_placeholder_regex, data_source.third_pass_placeholder_regex]:
1349 # 1) find placeholders in this line
1350 placeholders_in_line = regex.findall(placeholder_regex, line, regex.IGNORECASE)
1351 if len(placeholders_in_line) == 0:
1352 continue
1353 _log.debug('%s placeholders found with pattern: %s', len(placeholders_in_line), placeholder_regex)
1354 found_placeholders = True
1355 # 2) replace them
1356 for placeholder in placeholders_in_line:
1357 try:
1358 val = data_source[placeholder]
1359 except:
1360 _log.exception('error with placeholder [%s]', placeholder)
1361 val = gmTools.tex_escape_string(_('error with placeholder [%s]') % placeholder)
1362
1363 if val is None:
1364 _log.debug('error with placeholder [%s]', placeholder)
1365 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder)
1366
1367 line = line.replace(placeholder, val)
1368
1369 instance_file.write(line)
1370
1371 instance_file.close()
1372 self.re_editable_filenames = [output_filename]
1373 template_file.close()
1374
1375 return found_placeholders
1376 #--------------------------------------------------------
1378
1379 mimetypes = [
1380 'application/x-xetex',
1381 'application/x-latex',
1382 'application/x-tex',
1383 'text/plain'
1384 ]
1385
1386 for mimetype in mimetypes:
1387 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename)
1388 if editor_cmd is not None:
1389 break
1390
1391 if editor_cmd is None:
1392 # Xe(La)TeX code is utf8: also consider text *viewers*
1393 # since pretty much any of them will be an editor as well
1394 for mimetype in mimetypes:
1395 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename)
1396 if editor_cmd is not None:
1397 break
1398
1399 if editor_cmd is None:
1400 return False
1401
1402 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True)
1403 self.re_editable_filenames = [self.instance_filename]
1404 return result
1405 #--------------------------------------------------------
1407
1408 if instance_file is None:
1409 instance_file = self.instance_filename
1410
1411 try:
1412 open(instance_file, 'r').close()
1413 except:
1414 _log.exception('cannot access form instance file [%s]', instance_file)
1415 gmLog2.log_stack_trace()
1416 return None
1417
1418 self.instance_filename = instance_file
1419
1420 _log.debug('ignoring <format> directive [%s], generating PDF', format)
1421
1422 # Xe(La)TeX can need up to three runs to get cross references et al right
1423 if platform.system() == 'Windows':
1424 # not yet supported: -draftmode
1425 # does not support: -shell-escape
1426 draft_cmd = r'xelatex.exe -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1427 final_cmd = r'xelatex.exe -interaction=nonstopmode -output-directory=%s %s' % (self.__sandbox_dir, self.instance_filename)
1428 else:
1429 # not yet supported: -draftmode
1430 draft_cmd = r'xelatex -interaction=nonstopmode -output-directory=%s -shell-escape %s' % (self.__sandbox_dir, self.instance_filename)
1431 final_cmd = r'xelatex -interaction=nonstopmode -output-directory=%s -shell-escape %s' % (self.__sandbox_dir, self.instance_filename)
1432
1433 for run_cmd in [draft_cmd, draft_cmd, final_cmd]:
1434 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]):
1435 _log.error('problem running xelatex, cannot generate form output')
1436 gmDispatcher.send(signal = 'statustext', msg = _('Error running xelatex. Cannot turn Xe(La)TeX template into PDF.'), beep = True)
1437 return None
1438
1439 sandboxed_pdf_name = '%s.pdf' % os.path.splitext(self.instance_filename)[0]
1440 target_dir = os.path.normpath(os.path.join(os.path.split(sandboxed_pdf_name)[0], '..'))
1441 final_pdf_name = os.path.join (
1442 target_dir,
1443 os.path.split(sandboxed_pdf_name)[1]
1444 )
1445 _log.debug('copying sandboxed PDF: %s -> %s', sandboxed_pdf_name, final_pdf_name)
1446 try:
1447 shutil.copy2(sandboxed_pdf_name, target_dir)
1448 except IOError:
1449 _log.exception('cannot open/move sandboxed PDF')
1450 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True)
1451 return None
1452
1453 self.final_output_filenames = [final_pdf_name]
1454
1455 return final_pdf_name
1456
1457 #------------------------------------------------------------
1458 form_engines['X'] = cXeTeXForm
1459
1460 #============================================================
1461 # Gnuplot template forms
1462 #------------------------------------------------------------
1464 """A forms engine wrapping Gnuplot."""
1465
1466 #--------------------------------------------------------
1470 #--------------------------------------------------------
1472 """Allow editing the instance of the template."""
1473 self.re_editable_filenames = []
1474 return True
1475 #--------------------------------------------------------
1477 """Generate output suitable for further processing outside this class, e.g. printing.
1478
1479 Expects .data_filename to be set.
1480 """
1481 self.conf_filename = gmTools.get_unique_filename(prefix = 'gm2gpl-', suffix = '.conf')
1482 conf_file = io.open(self.conf_filename, mode = 'wt', encoding = 'utf8')
1483 conf_file.write('# setting the gnuplot data file\n')
1484 conf_file.write("gm2gpl_datafile = '%s'\n" % self.data_filename)
1485 conf_file.close()
1486
1487 # FIXME: cater for configurable path
1488 if platform.system() == 'Windows':
1489 exec_name = 'gnuplot.exe'
1490 else:
1491 exec_name = 'gnuplot'
1492
1493 args = [
1494 exec_name,
1495 '-p', # let plot window persist after main gnuplot process exits
1496 self.conf_filename, # contains the gm2gpl_datafile setting which, in turn, contains the actual data
1497 self.template_filename # contains the plotting instructions (IOW a user provided gnuplot script)
1498 ]
1499 _log.debug('plotting args: %s' % str(args))
1500
1501 try:
1502 gp = subprocess.Popen (
1503 args = args,
1504 close_fds = True
1505 )
1506 except (OSError, ValueError, subprocess.CalledProcessError):
1507 _log.exception('there was a problem executing gnuplot')
1508 gmDispatcher.send(signal = 'statustext', msg = _('Error running gnuplot. Cannot plot data.'), beep = True)
1509 return
1510
1511 gp.communicate()
1512
1513 self.final_output_filenames = [
1514 self.conf_filename,
1515 self.data_filename,
1516 self.template_filename
1517 ]
1518
1519 return
1520 #------------------------------------------------------------
1521 form_engines['G'] = cGnuplotForm
1522
1523 #============================================================
1524 # fPDF form engine
1525 #------------------------------------------------------------
1527 """A forms engine wrapping PDF forms.
1528
1529 Johann Felix Soden <johfel@gmx.de> helped with this.
1530
1531 http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf
1532
1533 http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/fdf_data_exchange.pdf
1534 """
1535
1537
1538 super(cPDFForm, self).__init__(template_file = template_file)
1539
1540 # detect pdftk
1541 found, self.pdftk_binary = gmShellAPI.detect_external_binary(binary = r'pdftk')
1542 if not found:
1543 raise ImportError('<pdftk(.exe)> not found')
1544 return # should be superfluous, actually
1545
1546 enc = sys.getfilesystemencoding()
1547 self.pdftk_binary = self.pdftk_binary.encode(enc)
1548
1549 base_name, ext = os.path.splitext(self.template_filename)
1550 self.fdf_dumped_filename = ('%s.fdf' % base_name).encode(enc)
1551 self.fdf_replaced_filename = ('%s-replaced.fdf' % base_name).encode(enc)
1552 self.pdf_filled_filename = ('%s-filled.pdf' % base_name).encode(enc)
1553 self.pdf_flattened_filename = ('%s-filled-flattened.pdf' % base_name).encode(enc)
1554 #--------------------------------------------------------
1556
1557 # dump form fields from template
1558 cmd_line = [
1559 self.pdftk_binary,
1560 self.template_filename,
1561 r'generate_fdf',
1562 r'output',
1563 self.fdf_dumped_filename
1564 ]
1565 _log.debug(' '.join(cmd_line))
1566 try:
1567 pdftk = subprocess.Popen(cmd_line)
1568 except OSError:
1569 _log.exception('cannot run <pdftk> (dump data from form)')
1570 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdftk. Cannot extract fields from PDF form template.'), beep = True)
1571 return False
1572
1573 pdftk.communicate()
1574 if pdftk.returncode != 0:
1575 _log.error('<pdftk> returned [%s], failed to dump data from PDF form into FDF', pdftk.returncode)
1576 return False
1577
1578 # parse dumped FDF file for "/V (...)" records
1579 # and replace placeholders therein
1580 fdf_dumped_file = io.open(self.fdf_dumped_filename, mode = 'rt', encoding = 'utf8')
1581 fdf_replaced_file = io.open(self.fdf_replaced_filename, mode = 'wt', encoding = 'utf8')
1582
1583 string_value_regex = r'\s*/V\s*\(.+\)\s*$'
1584 for line in fdf_dumped_file:
1585 if not regex.match(string_value_regex, line):
1586 fdf_replaced_file.write(line)
1587 continue
1588
1589 # strip cruft around the string value
1590 raw_str_val = line.strip() # remove framing whitespace
1591 raw_str_val = raw_str_val[2:] # remove leading "/V"
1592 raw_str_val = raw_str_val.lstrip() # remove whitespace between "/V" and "("
1593 raw_str_val = raw_str_val[1:] # remove opening "("
1594 raw_str_val = raw_str_val[2:] # remove BOM-16-BE
1595 raw_str_val = raw_str_val.rstrip() # remove trailing whitespace
1596 raw_str_val = raw_str_val[:-1] # remove closing ")"
1597
1598 # work on FDF escapes
1599 raw_str_val = raw_str_val.replace('\(', '(') # remove escaping of "("
1600 raw_str_val = raw_str_val.replace('\)', ')') # remove escaping of ")"
1601
1602 # by now raw_str_val should contain the actual
1603 # string value, albeit encoded as UTF-16, so
1604 # decode it into a unicode object,
1605 # split multi-line fields on "\n" literal
1606 raw_str_lines = raw_str_val.split('\x00\\n')
1607 value_template_lines = []
1608 for raw_str_line in raw_str_lines:
1609 value_template_lines.append(raw_str_line.decode('utf_16_be'))
1610
1611 replaced_lines = []
1612 for value_template in value_template_lines:
1613 # find any placeholders within
1614 placeholders_in_value = regex.findall(data_source.placeholder_regex, value_template, regex.IGNORECASE)
1615 for placeholder in placeholders_in_value:
1616 try:
1617 replacement = data_source[placeholder]
1618 except:
1619 _log.exception(replacement)
1620 replacement = _('error with placeholder [%s]') % placeholder
1621 if replacement is None:
1622 replacement = _('error with placeholder [%s]') % placeholder
1623 value_template = value_template.replace(placeholder, replacement)
1624
1625 value_template = value_template.encode('utf_16_be')
1626
1627 if len(placeholders_in_value) > 0:
1628 value_template = value_template.replace(r'(', r'\(')
1629 value_template = value_template.replace(r')', r'\)')
1630
1631 replaced_lines.append(value_template)
1632
1633 replaced_line = '\x00\\n'.join(replaced_lines)
1634
1635 fdf_replaced_file.write('/V (')
1636 fdf_replaced_file.write(codecs.BOM_UTF16_BE)
1637 fdf_replaced_file.write(replaced_line)
1638 fdf_replaced_file.write(')\n')
1639
1640 fdf_replaced_file.close()
1641 fdf_dumped_file.close()
1642
1643 # merge replaced data back into form
1644 cmd_line = [
1645 self.pdftk_binary,
1646 self.template_filename,
1647 r'fill_form',
1648 self.fdf_replaced_filename,
1649 r'output',
1650 self.pdf_filled_filename
1651 ]
1652 _log.debug(' '.join(cmd_line))
1653 try:
1654 pdftk = subprocess.Popen(cmd_line)
1655 except OSError:
1656 _log.exception('cannot run <pdftk> (merge data into form)')
1657 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdftk. Cannot fill in PDF form template.'), beep = True)
1658 return False
1659
1660 pdftk.communicate()
1661 if pdftk.returncode != 0:
1662 _log.error('<pdftk> returned [%s], failed to merge FDF data into PDF form', pdftk.returncode)
1663 return False
1664
1665 return True
1666 #--------------------------------------------------------
1668 mimetypes = [
1669 'application/pdf',
1670 'application/x-pdf'
1671 ]
1672
1673 for mimetype in mimetypes:
1674 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.pdf_filled_filename)
1675 if editor_cmd is not None:
1676 break
1677
1678 if editor_cmd is None:
1679 _log.debug('editor cmd not found, trying viewer cmd')
1680 for mimetype in mimetypes:
1681 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.pdf_filled_filename)
1682 if editor_cmd is not None:
1683 break
1684
1685 if editor_cmd is None:
1686 return False
1687
1688 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True)
1689
1690 path, fname = os.path.split(self.pdf_filled_filename)
1691 candidate = os.path.join(gmTools.gmPaths().home_dir, fname)
1692
1693 if os.access(candidate, os.R_OK):
1694 _log.debug('filled-in PDF found: %s', candidate)
1695 os.rename(self.pdf_filled_filename, self.pdf_filled_filename + '.bak')
1696 shutil.move(candidate, path)
1697 else:
1698 _log.debug('filled-in PDF not found: %s', candidate)
1699
1700 self.re_editable_filenames = [self.pdf_filled_filename]
1701
1702 return result
1703 #--------------------------------------------------------
1705 """Generate output suitable for further processing outside this class, e.g. printing."""
1706
1707 # eventually flatten the filled in form so we
1708 # can keep both a flattened and an editable copy:
1709 cmd_line = [
1710 self.pdftk_binary,
1711 self.pdf_filled_filename,
1712 r'output',
1713 self.pdf_flattened_filename,
1714 r'flatten'
1715 ]
1716 _log.debug(' '.join(cmd_line))
1717 try:
1718 pdftk = subprocess.Popen(cmd_line)
1719 except OSError:
1720 _log.exception('cannot run <pdftk> (flatten filled in form)')
1721 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdftk. Cannot flatten filled in PDF form.'), beep = True)
1722 return None
1723
1724 pdftk.communicate()
1725 if pdftk.returncode != 0:
1726 _log.error('<pdftk> returned [%s], failed to flatten filled in PDF form', pdftk.returncode)
1727 return None
1728
1729 self.final_output_filenames = [self.pdf_flattened_filename]
1730
1731 return self.pdf_flattened_filename
1732 #------------------------------------------------------------
1733 form_engines['P'] = cPDFForm
1734
1735 #============================================================
1736 # older code
1737 #------------------------------------------------------------
1739 """A forms engine wrapping LaTeX.
1740 """
1744
1746 try:
1747 latex = Cheetah.Template.Template (self.template, filter=LaTeXFilter, searchList=[params])
1748 # create a 'sandbox' directory for LaTeX to play in
1749 self.tmp = tempfile.mktemp ()
1750 os.makedirs (self.tmp)
1751 self.oldcwd = os.getcwd()
1752 os.chdir (self.tmp)
1753 stdin = os.popen ("latex", "w", 2048)
1754 stdin.write (str (latex)) #send text. LaTeX spits it's output into stdout
1755 # FIXME: send LaTeX output to the logger
1756 stdin.close ()
1757 if not gmShellAPI.run_command_in_shell("dvips texput.dvi -o texput.ps", blocking=True):
1758 raise FormError ('DVIPS returned error')
1759 except EnvironmentError as e:
1760 _log.error(e.strerror)
1761 raise FormError (e.strerror)
1762 return open("texput.ps")
1763
1765 """
1766 For testing purposes, runs Xdvi on the intermediate TeX output
1767 WARNING: don't try this on Windows
1768 """
1769 gmShellAPI.run_command_in_shell("xdvi texput.dvi", blocking=True)
1770
1772 if "%F" in command:
1773 command.replace ("%F", "texput.ps")
1774 else:
1775 command = "%s < texput.ps" % command
1776 try:
1777 if not gmShellAPI.run_command_in_shell(command, blocking=True):
1778 _log.error("external command %s returned non-zero" % command)
1779 raise FormError ('external command %s returned error' % command)
1780 except EnvironmentError as e:
1781 _log.error(e.strerror)
1782 raise FormError (e.strerror)
1783 return True
1784
1786 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print')
1787 self.exe (command)
1788
1790 """
1791 Delete all the LaTeX output iles
1792 """
1793 for i in os.listdir ('.'):
1794 os.unlink (i)
1795 os.chdir (self.oldcwd)
1796 os.rmdir (self.tmp)
1797
1798
1799
1800
1801 #================================================================
1802 # define a class for HTML forms (for printing)
1803 #================================================================
1805 """This class can create XML document from requested data,
1806 then process it with XSLT template and display results
1807 """
1808
1809 # FIXME: make the path configurable ?
1810 _preview_program = 'oowriter ' #this program must be in the system PATH
1811
1813
1814 if template is None:
1815 raise ValueError('%s: cannot create form instance without a template' % __name__)
1816
1817 cFormEngine.__init__(self, template = template)
1818
1819 self._FormData = None
1820
1821 # here we know/can assume that the template was stored as a utf-8
1822 # encoded string so use that conversion to create unicode:
1823 #self._XSLTData = str(str(template.template_data), 'UTF-8')
1824 # but in fact, str() knows how to handle buffers, so simply:
1825 self._XSLTData = str(self.template.template_data, 'UTF-8', 'strict')
1826
1827 # we must still devise a method of extracting the SQL query:
1828 # - either by retrieving it from a particular tag in the XSLT or
1829 # - by making the stored template actually be a dict which, unpickled,
1830 # has the keys "xslt" and "sql"
1831 self._SQL_query = 'select 1' #this sql query must output valid xml
1832 #--------------------------------------------------------
1833 # external API
1834 #--------------------------------------------------------
1836 """get data from backend and process it with XSLT template to produce readable output"""
1837
1838 # extract SQL (this is wrong but displays what is intended)
1839 xslt = libxml2.parseDoc(self._XSLTData)
1840 root = xslt.children
1841 for child in root:
1842 if child.type == 'element':
1843 self._SQL_query = child.content
1844 break
1845
1846 # retrieve data from backend
1847 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': self._SQL_query, 'args': sql_parameters}], get_col_idx = False)
1848
1849 __header = '<?xml version="1.0" encoding="UTF-8"?>\n'
1850 __body = rows[0][0]
1851
1852 # process XML data according to supplied XSLT, producing HTML
1853 self._XMLData =__header + __body
1854 style = libxslt.parseStylesheetDoc(xslt)
1855 xml = libxml2.parseDoc(self._XMLData)
1856 html = style.applyStylesheet(xml, None)
1857 self._FormData = html.serialize()
1858
1859 style.freeStylesheet()
1860 xml.freeDoc()
1861 html.freeDoc()
1862 #--------------------------------------------------------
1864 if self._FormData is None:
1865 raise ValueError('Preview request for empty form. Make sure the form is properly initialized and process() was performed')
1866
1867 fname = gmTools.get_unique_filename(prefix = 'gm_XSLT_form-', suffix = '.html')
1868 #html_file = os.open(fname, 'wb')
1869 #html_file.write(self._FormData.encode('UTF-8'))
1870 html_file = io.open(fname, mode = 'wt', encoding = 'utf8', errors = 'strict') # or 'replace' ?
1871 html_file.write(self._FormData)
1872 html_file.close()
1873
1874 cmd = '%s %s' % (self.__class__._preview_program, fname)
1875
1876 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = False):
1877 _log.error('%s: cannot launch report preview program' % __name__)
1878 return False
1879
1880 #os.unlink(self.filename) #delete file
1881 #FIXME: under Windows the temp file is deleted before preview program gets it (under Linux it works OK)
1882
1883 return True
1884 #--------------------------------------------------------
1888
1889
1890 #=====================================================
1891 #class LaTeXFilter(Cheetah.Filters.Filter):
1894 """
1895 Convience function to escape ISO-Latin-1 strings for TeX output
1896 WARNING: not all ISO-Latin-1 characters are expressible in TeX
1897 FIXME: nevertheless, there are a few more we could support
1898
1899 Also intelligently convert lists and tuples into TeX-style table lines
1900 """
1901 if type(item) is str:
1902 item = item.replace ("\\", "\\backslash") # I wonder about this, do we want users to be able to use raw TeX?
1903 item = item.replace ("&", "\\&")
1904 item = item.replace ("$", "\\$")
1905 item = item.replace ('"', "") # okay, that's not right, but easiest solution for now
1906 item = item.replace ("\n", "\\\\ ")
1907 if len (item.strip ()) == 0:
1908 item = "\\relax " # sometimes TeX really hates empty strings, this seems to mollify it
1909 # FIXME: cover all of ISO-Latin-1 which can be expressed in TeX
1910 item = item.encode ('latin-1', 'replace')
1911 trans = {'ß':'\\ss{}', 'ä': '\\"{a}', 'Ä' :'\\"{A}', 'ö': '\\"{o}', 'Ö': '\\"{O}', 'ü': '\\"{u}', 'Ü': '\\"{U}',
1912 '\x8a':'\\v{S}', '\x8a':'\\OE{}', '\x9a':'\\v{s}', '\x9c': '\\oe{}', '\a9f':'\\"{Y}', #Microsloth extensions
1913 '\x86': '{\\dag}', '\x87': '{\\ddag}', '\xa7':'{\\S}', '\xb6': '{\\P}', '\xa9': '{\\copyright}', '\xbf': '?`',
1914 '\xc0':'\\`{A}', '\xa1': "\\'{A}", '\xa2': '\\^{A}', '\xa3':'\\~{A}', '\\xc5': '{\AA}',
1915 '\xc7':'\\c{C}', '\xc8':'\\`{E}',
1916 '\xa1': '!`',
1917 '\xb5':'$\mu$', '\xa3': '\pounds{}', '\xa2':'cent'
1918 }
1919 for k, i in trans.items ():
1920 item = item.replace (k, i)
1921 elif type(item) is list or type(item) is tuple:
1922 item = string.join ([self.conv_enc(i, ' & ') for i in item], table_sep)
1923 elif item is None:
1924 item = '\\relax % Python None\n'
1925 elif type(item) is int or type(item) is float:
1926 item = str(item)
1927 else:
1928 item = str(item)
1929 _log.warning("unknown type %s, string %s" % (type(item), item))
1930 return item
1931
1932
1933 #===========================================================
1936
1937 #============================================================
1938 # convenience functions
1939 #------------------------------------------------------------
1941 """
1942 Instantiates a FormEngine based on the form ID or name from the backend
1943 """
1944 try:
1945 # it's a number: match to form ID
1946 id = int (id)
1947 cmd = 'select template, engine, pk from paperwork_templates where pk = %s'
1948 except ValueError:
1949 # it's a string, match to the form's name
1950 # FIXME: can we somehow OR like this: where name_short=%s OR name_long=%s ?
1951 cmd = 'select template, engine, flags, pk from paperwork_templates where name_short = %s'
1952 result = gmPG.run_ro_query ('reference', cmd, None, id)
1953 if result is None:
1954 _log.error('error getting form [%s]' % id)
1955 raise gmExceptions.FormError ('error getting form [%s]' % id)
1956 if len(result) == 0:
1957 _log.error('no form [%s] found' % id)
1958 raise gmExceptions.FormError ('no such form found [%s]' % id)
1959 if result[0][1] == 'L':
1960 return LaTeXForm (result[0][2], result[0][0])
1961 elif result[0][1] == 'T':
1962 return TextForm (result[0][2], result[0][0])
1963 else:
1964 _log.error('no form engine [%s] for form [%s]' % (result[0][1], id))
1965 raise FormError ('no engine [%s] for form [%s]' % (result[0][1], id))
1966 #-------------------------------------------------------------
1973 #-------------------------------------------------------------
1974
1975 test_letter = """
1976 \\documentclass{letter}
1977 \\address{ $DOCTOR \\\\
1978 $DOCTORADDRESS}
1979 \\signature{$DOCTOR}
1980
1981 \\begin{document}
1982 \\begin{letter}{$RECIPIENTNAME \\\\
1983 $RECIPIENTADDRESS}
1984
1985 \\opening{Dear $RECIPIENTNAME}
1986
1987 \\textbf{Re:} $PATIENTNAME, DOB: $DOB, $PATIENTADDRESS \\\\
1988
1989 $TEXT
1990
1991 \\ifnum$INCLUDEMEDS>0
1992 \\textbf{Medications List}
1993
1994 \\begin{tabular}{lll}
1995 $MEDSLIST
1996 \\end{tabular}
1997 \\fi
1998
1999 \\ifnum$INCLUDEDISEASES>0
2000 \\textbf{Disease List}
2001
2002 \\begin{tabular}{l}
2003 $DISEASELIST
2004 \\end{tabular}
2005 \\fi
2006
2007 \\closing{$CLOSING}
2008
2009 \\end{letter}
2010 \\end{document}
2011 """
2012
2013
2015 f = io.open('../../test-area/ian/terry-form.tex')
2016 params = {
2017 'RECIPIENT': "Dr. R. Terry\n1 Main St\nNewcastle",
2018 'DOCTORSNAME': 'Ian Haywood',
2019 'DOCTORSADDRESS': '1 Smith St\nMelbourne',
2020 'PATIENTNAME':'Joe Bloggs',
2021 'PATIENTADDRESS':'18 Fred St\nMelbourne',
2022 'REQUEST':'echocardiogram',
2023 'THERAPY':'on warfarin',
2024 'CLINICALNOTES':"""heard new murmur
2025 Here's some
2026 crap to demonstrate how it can cover multiple lines.""",
2027 'COPYADDRESS':'Jack Jones\nHannover, Germany',
2028 'ROUTINE':1,
2029 'URGENT':0,
2030 'FAX':1,
2031 'PHONE':1,
2032 'PENSIONER':1,
2033 'VETERAN':0,
2034 'PADS':0,
2035 'INSTRUCTIONS':'Take the blue pill, Neo'
2036 }
2037 form = LaTeXForm (1, f.read())
2038 form.process (params)
2039 form.xdvi ()
2040 form.cleanup ()
2041
2043 form = LaTeXForm (2, test_letter)
2044 params = {'RECIPIENTNAME':'Dr. Richard Terry',
2045 'RECIPIENTADDRESS':'1 Main St\nNewcastle',
2046 'DOCTOR':'Dr. Ian Haywood',
2047 'DOCTORADDRESS':'1 Smith St\nMelbourne',
2048 'PATIENTNAME':'Joe Bloggs',
2049 'PATIENTADDRESS':'18 Fred St, Melbourne',
2050 'TEXT':"""This is the main text of the referral letter""",
2051 'DOB':'12/3/65',
2052 'INCLUDEMEDS':1,
2053 'MEDSLIST':[["Amoxycillin", "500mg", "TDS"], ["Perindopril", "4mg", "OD"]],
2054 'INCLUDEDISEASES':0, 'DISEASELIST':'',
2055 'CLOSING':'Yours sincerely,'
2056 }
2057 form.process (params)
2058 print(os.getcwd())
2059 form.xdvi()
2060 form.cleanup()
2061
2062 #------------------------------------------------------------
2064 template = io.open('../../test-area/ian/Formularkopf-DE.tex')
2065 form = LaTeXForm(template=template.read())
2066 params = {
2067 'PATIENT LASTNAME': 'Kirk',
2068 'PATIENT FIRSTNAME': 'James T.',
2069 'PATIENT STREET': 'Hauptstrasse',
2070 'PATIENT ZIP': '02999',
2071 'PATIENT TOWN': 'Gross Saerchen',
2072 'PATIENT DOB': '22.03.1931'
2073 }
2074 form.process(params)
2075 form.xdvi()
2076 form.cleanup()
2077
2078 #============================================================
2079 # main
2080 #------------------------------------------------------------
2081 if __name__ == '__main__':
2082
2083 if len(sys.argv) < 2:
2084 sys.exit()
2085
2086 if sys.argv[1] != 'test':
2087 sys.exit()
2088
2089 gmDateTime.init()
2090
2091 #--------------------------------------------------------
2092 # OOo
2093 #--------------------------------------------------------
2095 init_ooo()
2096 #--------------------------------------------------------
2101 #--------------------------------------------------------
2103 srv = gmOOoConnector()
2104 doc = srv.open_document(filename = sys.argv[2])
2105 print("document:", doc)
2106 #--------------------------------------------------------
2108 doc = cOOoLetter(template_file = sys.argv[2])
2109 doc.open_in_ooo()
2110 print("document:", doc)
2111 input('press <ENTER> to continue')
2112 doc.show()
2113 #doc.replace_placeholders()
2114 #doc.save_in_ooo('~/test_cOOoLetter.odt')
2115 # doc = None
2116 # doc.close_in_ooo()
2117 input('press <ENTER> to continue')
2118 #--------------------------------------------------------
2120 try:
2121 doc = open_uri_in_ooo(filename=sys.argv[1])
2122 except:
2123 _log.exception('cannot open [%s] in OOo' % sys.argv[1])
2124 raise
2125
2126 class myCloseListener(unohelper.Base, oooXCloseListener):
2127 def disposing(self, evt):
2128 print("disposing:")
2129 def notifyClosing(self, evt):
2130 print("notifyClosing:")
2131 def queryClosing(self, evt, owner):
2132 # owner is True/False whether I am the owner of the doc
2133 print("queryClosing:")
2134
2135 l = myCloseListener()
2136 doc.addCloseListener(l)
2137
2138 tfs = doc.getTextFields().createEnumeration()
2139 print(tfs)
2140 print(dir(tfs))
2141 while tfs.hasMoreElements():
2142 tf = tfs.nextElement()
2143 if tf.supportsService('com.sun.star.text.TextField.JumpEdit'):
2144 print(tf.getPropertyValue('PlaceHolder'))
2145 print(" ", tf.getPropertyValue('Hint'))
2146
2147 # doc.close(True) # closes but leaves open the dedicated OOo window
2148 doc.dispose() # closes and disposes of the OOo window
2149 #--------------------------------------------------------
2151 pat = gmPersonSearch.ask_for_patient()
2152 if pat is None:
2153 return
2154 gmPerson.set_active_patient(patient = pat)
2155
2156 doc = cOOoLetter(template_file = sys.argv[2])
2157 doc.open_in_ooo()
2158 print(doc)
2159 doc.show()
2160 #doc.replace_placeholders()
2161 #doc.save_in_ooo('~/test_cOOoLetter.odt')
2162 doc = None
2163 # doc.close_in_ooo()
2164 input('press <ENTER> to continue')
2165 #--------------------------------------------------------
2166 # other
2167 #--------------------------------------------------------
2169 template = cFormTemplate(aPK_obj = sys.argv[2])
2170 print(template)
2171 print(template.save_to_file())
2172 #--------------------------------------------------------
2174 template = cFormTemplate(aPK_obj = sys.argv[2])
2175 template.update_template_from_file(filename = sys.argv[3])
2176 #--------------------------------------------------------
2178 pat = gmPersonSearch.ask_for_patient()
2179 if pat is None:
2180 return
2181 gmPerson.set_active_patient(patient = pat)
2182
2183 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
2184
2185 path = os.path.abspath(sys.argv[2])
2186 form = cLaTeXForm(template_file = path)
2187
2188 from Gnumed.wxpython import gmMacro
2189 ph = gmMacro.gmPlaceholderHandler()
2190 ph.debug = True
2191 instance_file = form.substitute_placeholders(data_source = ph)
2192 pdf_name = form.generate_output(instance_file = instance_file)
2193 print("final PDF file is:", pdf_name)
2194 #--------------------------------------------------------
2196 pat = gmPersonSearch.ask_for_patient()
2197 if pat is None:
2198 return
2199 gmPerson.set_active_patient(patient = pat)
2200
2201 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
2202
2203 path = os.path.abspath(sys.argv[2])
2204 form = cPDFForm(template_file = path)
2205
2206 from Gnumed.wxpython import gmMacro
2207 ph = gmMacro.gmPlaceholderHandler()
2208 ph.debug = True
2209 instance_file = form.substitute_placeholders(data_source = ph)
2210 pdf_name = form.generate_output(instance_file = instance_file)
2211 print("final PDF file is:", pdf_name)
2212 #--------------------------------------------------------
2214 pat = gmPersonSearch.ask_for_patient()
2215 if pat is None:
2216 return
2217 gmPerson.set_active_patient(patient = pat)
2218
2219 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
2220
2221 path = os.path.abspath(sys.argv[2])
2222 form = cAbiWordForm(template_file = path)
2223
2224 from Gnumed.wxpython import gmMacro
2225 ph = gmMacro.gmPlaceholderHandler()
2226 ph.debug = True
2227 instance_file = form.substitute_placeholders(data_source = ph)
2228 form.edit()
2229 final_name = form.generate_output(instance_file = instance_file)
2230 print("final file is:", final_name)
2231 #--------------------------------------------------------
2233
2234 from Gnumed.business import gmPraxis
2235
2236 branches = gmPraxis.get_praxis_branches()
2237 praxis = gmPraxis.gmCurrentPraxisBranch(branches[0])
2238 print(praxis)
2239
2240 pat = gmPersonSearch.ask_for_patient()
2241 if pat is None:
2242 return
2243 gmPerson.set_active_patient(patient = pat)
2244
2245 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
2246
2247 path = os.path.abspath(sys.argv[2])
2248 form = cTextForm(template_file = path)
2249
2250 from Gnumed.wxpython import gmMacro
2251 ph = gmMacro.gmPlaceholderHandler()
2252 ph.debug = True
2253 print("placeholder substitution worked:", form.substitute_placeholders(data_source = ph))
2254 print(form.re_editable_filenames)
2255 form.edit()
2256 form.generate_output()
2257 #--------------------------------------------------------
2258 #--------------------------------------------------------
2259 #--------------------------------------------------------
2260 # now run the tests
2261 #test_au()
2262 #test_de()
2263
2264 # OOo
2265 #test_init_ooo()
2266 #test_ooo_connect()
2267 #test_open_ooo_doc_from_srv()
2268 #test_open_ooo_doc_from_letter()
2269 #play_with_ooo()
2270 #test_cOOoLetter()
2271
2272 #test_cFormTemplate()
2273 #set_template_from_file()
2274 #test_latex_form()
2275 #test_pdf_form()
2276 #test_abiword_form()
2277 test_text_form()
2278
2279 #============================================================
2280
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sun Aug 19 01:55:20 2018 | http://epydoc.sourceforge.net |