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