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 #============================================================7184 #============================================================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])8699 #============================================================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])101117 #============================================================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])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 #--------------------------------------------------------225 #============================================================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 filename211 #--------------------------------------------------------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)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 templates266 #------------------------------------------------------------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 template279 #------------------------------------------------------------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 True287 #============================================================ 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 = document359 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 !489 #------------------------------------------------------------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 = None424 #--------------------------------------------------------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 doc450 #-------------------------------------------------------- 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.__desktop487 488 desktop = property(_get_desktop, lambda x:x)491596 #-------------------------------------------------------- 597 # internal helpers 598 #-------------------------------------------------------- 599 600 #============================================================493 494 self.template_file = template_file 495 self.instance_type = instance_type 496 self.ooo_doc = None497 #-------------------------------------------------------- 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 True517 #-------------------------------------------------------- 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 = None584 #--------------------------------------------------------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 = None602 """Ancestor for forms.""" 603 606 #--------------------------------------------------------685 686 #================================================================ 687 # OOo template forms 688 #----------------------------------------------------------------608 """Parse the template into an instance and replace placeholders with values.""" 609 raise NotImplementedError610 #-------------------------------------------------------- 614 #--------------------------------------------------------616 """Generate output suitable for further processing outside this class, e.g. printing.""" 617 raise NotImplementedError618 #-------------------------------------------------------- 623 #--------------------------------------------------------625 """ 626 A sop to TeX which can't act as a true filter: to delete temporary files 627 """ 628 pass629 #--------------------------------------------------------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 pass637 #--------------------------------------------------------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 status690 """A forms engine wrapping OOo.""" 691700 701 #================================================================ 702 # LaTeX template forms 703 #----------------------------------------------------------------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)705 """A forms engine wrapping LaTeX.""" 706834 #------------------------------------------------------------ 835 form_engines[u'L'] = cLaTeXForm 836 #============================================================ 837 # Gnuplot template forms 838 #------------------------------------------------------------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 return747 #--------------------------------------------------------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 result768 #--------------------------------------------------------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_name840 """A forms engine wrapping Gnuplot.""" 841 842 #-------------------------------------------------------- 846 #--------------------------------------------------------891 #------------------------------------------------------------ 892 form_engines[u'G'] = cGnuplotForm 893 894 #============================================================ 895 # fPDF form engine 896 #------------------------------------------------------------848 """Allow editing the instance of the template.""" 849 self.re_editable_filenames = [] 850 return True851 #--------------------------------------------------------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 return898 """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 """ 9061103 #------------------------------------------------------------ 1104 form_engines[u'P'] = cPDFForm 1105 1106 #============================================================ 1107 # older code 1108 #------------------------------------------------------------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 True1037 #--------------------------------------------------------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 result1074 #--------------------------------------------------------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_filename1110 """A forms engine wrapping LaTeX. 1111 """ 11151168 1169 1170 1171 1172 #================================================================ 1173 # define a class for HTML forms (for printing) 1174 #================================================================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")11341136 """ 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)11411143 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 True11551157 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print') 1158 self.exe (command)11591176 """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 11821259 1260 1261 #===================================================== 1262 #class LaTeXFilter(Cheetah.Filters.Filter):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 xml1203 #-------------------------------------------------------- 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 True1255 #--------------------------------------------------------1302 1303 1304 #=========================================================== 1307 1308 #============================================================ 1309 # convenience functions 1310 #------------------------------------------------------------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 item1312 """ 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 13841386 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 ()14121414 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:", doc1479 #--------------------------------------------------------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_name1567 #--------------------------------------------------------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_name1585 #-------------------------------------------------------- 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 |