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