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