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