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