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