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