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'\$<.+?>\$' 749820 #---------------------------------------------------------------- 821 form_engines[u'A'] = cAbiWordForm 822 823 #================================================================ 824 # text template forms 825 #----------------------------------------------------------------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 for line in template_file: 777 778 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 779 instance_file.write(line) 780 continue 781 782 # 1) find placeholders in this line 783 placeholders_in_line = regex.findall(cAbiWordForm.placeholder_regex, line, regex.IGNORECASE) 784 # 2) and replace them 785 for placeholder in placeholders_in_line: 786 try: 787 val = data_source[placeholder.replace(u'<', u'<').replace(u'>', u'>')] 788 except: 789 val = _('error with placeholder [%s]') % gmTools.xml_escape_string(placeholder) 790 _log.exception(val) 791 792 if val is None: 793 val = _('error with placeholder [%s]') % gmTools.xml_escape_string(placeholder) 794 795 line = line.replace(placeholder, val) 796 797 instance_file.write(line) 798 799 instance_file.close() 800 template_file.close() 801 802 if self.template is not None: 803 # remove temporary placeholders 804 data_source.unset_placeholder(u'form_name_long') 805 data_source.unset_placeholder(u'form_name_short') 806 data_source.unset_placeholder(u'form_version') 807 808 return809 #--------------------------------------------------------811 enc = sys.getfilesystemencoding() 812 cmd = (r'%s %s' % (self.abiword_binary, self.instance_filename.encode(enc))).encode(enc) 813 result = gmShellAPI.run_command_in_shell(command = cmd, blocking = True) 814 self.re_editable_filenames = [] 815 return result816 #--------------------------------------------------------827 """A forms engine outputting data as text for further processing.""" 828929 #------------------------------------------------------------ 930 form_engines[u'T'] = cTextForm 931 932 #================================================================ 933 # LaTeX template forms 934 #----------------------------------------------------------------830 super(self.__class__, self).__init__(template_file = template_file) 831 832 # generate real template file from .ini file 833 cfg_file = codecs.open(filename = self.template_filename, mode = 'rU', encoding = u'utf8') 834 self.form_definition = gmCfg2.parse_INI_stream(stream = cfg_file) 835 cfg_file.close() 836 self.form_definition['form::template']837 #--------------------------------------------------------839 self.instance_filename = gmTools.get_unique_filename ( 840 prefix = 'gm-T-instance-', 841 suffix = '.txt' 842 ) 843 instance_file = codecs.open(self.instance_filename, 'wb', 'utf8') 844 845 if self.template is not None: 846 # inject placeholder values 847 data_source.set_placeholder(u'form_name_long', self.template['name_long']) 848 data_source.set_placeholder(u'form_name_short', self.template['name_short']) 849 data_source.set_placeholder(u'form_version', self.template['external_version']) 850 851 if isinstance(self.form_definition['form::template'], type([])): 852 template_text = self.form_definition['form::template'] 853 else: 854 template_text = self.form_definition['form::template'].split('\n') 855 856 no_errors = True 857 for line in template_text: 858 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 859 instance_file.write('%s\n' % line) 860 continue 861 862 # 1) find placeholders in this line 863 placeholders_in_line = regex.findall(data_source.placeholder_regex, line, regex.IGNORECASE) 864 # 2) and replace them 865 for placeholder in placeholders_in_line: 866 try: 867 val = data_source[placeholder] 868 except: 869 val = _('error with placeholder [%s]') % placeholder 870 _log.exception(val) 871 no_errors = False 872 873 if val is None: 874 val = _('error with placeholder [%s]') % placeholder 875 876 line = line.replace(placeholder, val) 877 878 instance_file.write(u'%s\n' % line) 879 880 instance_file.close() 881 self.re_editable_filenames = [self.instance_filename] 882 883 if self.template is not None: 884 # remove temporary placeholders 885 data_source.unset_placeholder(u'form_name_long') 886 data_source.unset_placeholder(u'form_name_short') 887 data_source.unset_placeholder(u'form_version') 888 889 return no_errors890 #--------------------------------------------------------892 893 editor_cmd = None 894 try: 895 editor_cmd = self.form_definition['form::editor'] % self.instance_filename 896 except KeyError: 897 _log.debug('no explicit editor defined for text template') 898 899 if editor_cmd is None: 900 mimetype = u'text/plain' 901 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename) 902 if editor_cmd is None: 903 # also consider text *viewers* since pretty much any of them will be an editor as well 904 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename) 905 906 # last resort 907 # if editor_cmd is None: 908 # if os.name == 'nt': 909 # editor_cmd = u'notepad.exe %s' % self.instance_filename 910 # else: 911 # editor_cmd = u'sensible-editor %s' % self.instance_filename 912 913 if editor_cmd is not None: 914 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 915 self.re_editable_filenames = [self.instance_filename] 916 917 return result918 #--------------------------------------------------------920 try: 921 post_processor = self.form_definition['form::post processor'] % self.instance_filename 922 except KeyError: 923 _log.debug('no explicit post processor defined for text template') 924 return True 925 926 self.final_output_filenames = [self.instance_filename] 927 928 return gmShellAPI.run_command_in_shell(command = post_processor, blocking = True)936 """A forms engine wrapping LaTeX.""" 9371115 #------------------------------------------------------------ 1116 form_engines[u'L'] = cLaTeXForm 1117 1118 #================================================================ 1119 # Xe(La)TeX template forms 1120 #---------------------------------------------------------------- 1121 # Xe(La)TeX: http://www.scholarsfonts.net/xetextt.pdf939 super(self.__class__, self).__init__(template_file = template_file)940 #--------------------------------------------------------942 943 if self.template is not None: 944 # inject placeholder values 945 data_source.set_placeholder(u'form_name_long', self.template['name_long']) 946 data_source.set_placeholder(u'form_name_short', self.template['name_short']) 947 data_source.set_placeholder(u'form_version', self.template['external_version']) 948 949 path, ext = os.path.splitext(self.template_filename) 950 if ext in [r'', r'.']: 951 ext = r'.tex' 952 953 filenames = [ 954 self.template_filename, 955 r'%s-run1_result%s' % (path, ext), 956 r'%s-run2_result%s' % (path, ext), 957 r'%s-run3_result%s' % (path, ext) 958 ] 959 960 found_placeholders = True 961 current_run = 1 962 while found_placeholders and (current_run < 4): 963 _log.debug('placeholder substitution run #%s', current_run) 964 found_placeholders = self.__substitute_placeholders ( 965 input_filename = filenames[current_run-1], 966 output_filename = filenames[current_run], 967 data_source = data_source 968 ) 969 current_run += 1 970 971 if self.template is not None: 972 # remove temporary placeholders 973 data_source.unset_placeholder(u'form_name_long') 974 data_source.unset_placeholder(u'form_name_short') 975 data_source.unset_placeholder(u'form_version') 976 977 self.instance_filename = self.re_editable_filenames[0] 978 979 return980 #--------------------------------------------------------981 - def __substitute_placeholders(self, data_source=None, input_filename=None, output_filename=None):982 983 _log.debug('[%s] -> [%s]', input_filename, output_filename) 984 985 found_placeholders = False 986 987 template_file = codecs.open(input_filename, 'rU', 'utf8') 988 instance_file = codecs.open(output_filename, 'wb', 'utf8') 989 990 for line in template_file: 991 992 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 993 instance_file.write(line) 994 continue 995 996 # 1) find placeholders in this line 997 placeholders_in_line = regex.findall(data_source.placeholder_regex, line, regex.IGNORECASE) 998 if len(placeholders_in_line) > 0: 999 found_placeholders = True 1000 # 2) and replace them 1001 for placeholder in placeholders_in_line: 1002 try: 1003 val = data_source[placeholder] 1004 except: 1005 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder) 1006 _log.exception(val) 1007 1008 if val is None: 1009 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder) 1010 1011 line = line.replace(placeholder, val) 1012 1013 instance_file.write(line) 1014 1015 instance_file.close() 1016 self.re_editable_filenames = [output_filename] 1017 template_file.close() 1018 1019 return found_placeholders1020 #--------------------------------------------------------1022 1023 mimetypes = [ 1024 u'application/x-latex', 1025 u'application/x-tex', 1026 u'text/plain' 1027 ] 1028 1029 for mimetype in mimetypes: 1030 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename) 1031 if editor_cmd is not None: 1032 break 1033 1034 if editor_cmd is None: 1035 # LaTeX code is text: also consider text *viewers* 1036 # since pretty much any of them will be an editor as well 1037 for mimetype in mimetypes: 1038 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename) 1039 if editor_cmd is not None: 1040 break 1041 1042 # last resort 1043 # if editor_cmd is None: 1044 # if os.name == 'nt': 1045 # editor_cmd = u'notepad.exe %s' % self.instance_filename 1046 # else: 1047 # editor_cmd = u'sensible-editor %s' % self.instance_filename 1048 1049 if editor_cmd is not None: 1050 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 1051 self.re_editable_filenames = [self.instance_filename] 1052 1053 return result1054 #--------------------------------------------------------1056 1057 if instance_file is None: 1058 instance_file = self.instance_filename 1059 1060 try: 1061 open(instance_file, 'r').close() 1062 except: 1063 _log.exception('cannot access form instance file [%s]', instance_file) 1064 gmLog2.log_stack_trace() 1065 return None 1066 1067 self.instance_filename = instance_file 1068 1069 _log.debug('ignoring <format> directive [%s], generating PDF', format) 1070 1071 # create sandbox for LaTeX to play in 1072 sandbox_dir = os.path.splitext(self.template_filename)[0] 1073 _log.debug('LaTeX sandbox directory: [%s]', sandbox_dir) 1074 1075 gmTools.mkdir(sandbox_dir) 1076 1077 sandboxed_instance_filename = os.path.join(sandbox_dir, os.path.split(self.instance_filename)[1]) 1078 shutil.move(self.instance_filename, sandboxed_instance_filename) 1079 self.re_editable_filenames = [sandboxed_instance_filename] 1080 1081 # LaTeX can need up to three runs to get cross references et al right 1082 if platform.system() == 'Windows': 1083 draft_cmd = r'pdflatex.exe -draftmode -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 1084 final_cmd = r'pdflatex.exe -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 1085 else: 1086 draft_cmd = r'pdflatex -draftmode -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 1087 final_cmd = r'pdflatex -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 1088 for run_cmd in [draft_cmd, draft_cmd, final_cmd]: 1089 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]): 1090 _log.error('problem running pdflatex, cannot generate form output') 1091 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdflatex. Cannot turn LaTeX template into PDF.'), beep = True) 1092 return None 1093 1094 sandboxed_pdf_name = u'%s.pdf' % os.path.splitext(sandboxed_instance_filename)[0] 1095 target_dir = os.path.split(self.instance_filename)[0] 1096 try: 1097 shutil.move(sandboxed_pdf_name, target_dir) 1098 except IOError: 1099 _log.exception('cannot move sandboxed PDF: %s -> %s', sandboxed_pdf_name, target_dir) 1100 gmDispatcher.send(signal = 'statustext', msg = _('Sandboxed PDF output file cannot be moved.'), beep = True) 1101 return None 1102 1103 final_pdf_name = u'%s.pdf' % os.path.splitext(self.instance_filename)[0] 1104 1105 try: 1106 open(final_pdf_name, 'r').close() 1107 except IOError: 1108 _log.exception('cannot open target PDF: %s', final_pdf_name) 1109 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True) 1110 return None 1111 1112 self.final_output_filenames = [final_pdf_name] 1113 1114 return final_pdf_name1123 """A forms engine wrapping Xe(La)TeX.""" 11241309 #------------------------------------------------------------ 1310 form_engines[u'X'] = cXeTeXForm 1311 1312 #============================================================ 1313 # Gnuplot template forms 1314 #------------------------------------------------------------1126 super(self.__class__, self).__init__(template_file = template_file)1127 # path, ext = os.path.splitext(self.template_filename) 1128 # if ext in [r'', r'.']: 1129 # ext = r'.tex' 1130 # self.instance_filename = r'%s-instance%s' % (path, ext) 1131 #--------------------------------------------------------1133 1134 if self.template is not None: 1135 # inject placeholder values 1136 data_source.set_placeholder(u'form_name_long', self.template['name_long']) 1137 data_source.set_placeholder(u'form_name_short', self.template['name_short']) 1138 data_source.set_placeholder(u'form_version', self.template['external_version']) 1139 1140 path, ext = os.path.splitext(self.template_filename) 1141 if ext in [r'', r'.']: 1142 ext = r'.tex' 1143 1144 filenames = [ 1145 self.template_filename, 1146 r'%s-run1_result%s' % (path, ext), 1147 r'%s-run2_result%s' % (path, ext), 1148 r'%s-run3_result%s' % (path, ext) 1149 ] 1150 1151 found_placeholders = True 1152 current_run = 1 1153 while found_placeholders and (current_run < 4): 1154 _log.debug('placeholder substitution run #%s', current_run) 1155 found_placeholders = self.__substitute_placeholders ( 1156 input_filename = filenames[current_run-1], 1157 output_filename = filenames[current_run], 1158 data_source = data_source 1159 ) 1160 current_run += 1 1161 1162 if self.template is not None: 1163 # remove temporary placeholders 1164 data_source.unset_placeholder(u'form_name_long') 1165 data_source.unset_placeholder(u'form_name_short') 1166 data_source.unset_placeholder(u'form_version') 1167 1168 self.instance_filename = self.re_editable_filenames[0] 1169 1170 return1171 #--------------------------------------------------------1172 - def __substitute_placeholders(self, data_source=None, input_filename=None, output_filename=None):1173 _log.debug('[%s] -> [%s]', input_filename, output_filename) 1174 1175 found_placeholders = False 1176 1177 template_file = codecs.open(input_filename, 'rU', 'utf8') 1178 instance_file = codecs.open(output_filename, 'wb', 'utf8') 1179 1180 for line in template_file: 1181 1182 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 1183 instance_file.write(line) 1184 continue 1185 1186 # 1) find placeholders in this line 1187 placeholders_in_line = regex.findall(data_source.placeholder_regex, line, regex.IGNORECASE) 1188 if len(placeholders_in_line) > 0: 1189 found_placeholders = True 1190 # 2) and replace them 1191 for placeholder in placeholders_in_line: 1192 try: 1193 val = data_source[placeholder] 1194 except: 1195 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder, replace_known_unicode=False) 1196 _log.exception(val) 1197 1198 if val is None: 1199 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder, replace_known_unicode=False) 1200 1201 line = line.replace(placeholder, val) 1202 1203 instance_file.write(line) 1204 1205 instance_file.close() 1206 self.re_editable_filenames = [output_filename] 1207 template_file.close() 1208 1209 return found_placeholders1210 #--------------------------------------------------------1212 1213 mimetypes = [ 1214 u'application/x-xetex', 1215 u'application/x-latex', 1216 u'application/x-tex', 1217 u'text/plain' 1218 ] 1219 1220 for mimetype in mimetypes: 1221 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename) 1222 if editor_cmd is not None: 1223 break 1224 1225 if editor_cmd is None: 1226 # Xe(La)TeX code is utf8: also consider text *viewers* 1227 # since pretty much any of them will be an editor as well 1228 for mimetype in mimetypes: 1229 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename) 1230 if editor_cmd is not None: 1231 break 1232 1233 # last resort 1234 # if editor_cmd is None: 1235 # if os.name == 'nt': 1236 # editor_cmd = u'notepad.exe %s' % self.instance_filename 1237 # else: 1238 # editor_cmd = u'sensible-editor %s' % self.instance_filename 1239 1240 if editor_cmd is not None: 1241 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 1242 self.re_editable_filenames = [self.instance_filename] 1243 1244 return result1245 #--------------------------------------------------------1247 1248 if instance_file is None: 1249 instance_file = self.instance_filename 1250 1251 try: 1252 open(instance_file, 'r').close() 1253 except: 1254 _log.exception('cannot access form instance file [%s]', instance_file) 1255 gmLog2.log_stack_trace() 1256 return None 1257 1258 self.instance_filename = instance_file 1259 1260 _log.debug('ignoring <format> directive [%s], generating PDF', format) 1261 1262 # create sandbox for Xe(La)TeX to play in 1263 sandbox_dir = os.path.splitext(self.template_filename)[0] 1264 _log.debug('Xe(La)TeX sandbox directory: [%s]', sandbox_dir) 1265 1266 gmTools.mkdir(sandbox_dir) 1267 1268 sandboxed_instance_filename = os.path.join(sandbox_dir, os.path.split(self.instance_filename)[1]) 1269 shutil.move(self.instance_filename, sandboxed_instance_filename) 1270 self.re_editable_filenames = [sandboxed_instance_filename] 1271 1272 # Xe(La)TeX can need up to three runs to get cross references et al right 1273 if platform.system() == 'Windows': 1274 # not yet supported: -draftmode 1275 # does not support: -shell-escape 1276 draft_cmd = r'xelatex.exe -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 1277 final_cmd = r'xelatex.exe -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 1278 else: 1279 # not yet supported: -draftmode 1280 draft_cmd = r'xelatex -interaction=nonstopmode -output-directory=%s -shell-escape %s' % (sandbox_dir, sandboxed_instance_filename) 1281 final_cmd = r'xelatex -interaction=nonstopmode -output-directory=%s -shell-escape %s' % (sandbox_dir, sandboxed_instance_filename) 1282 for run_cmd in [draft_cmd, draft_cmd, final_cmd]: 1283 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]): 1284 _log.error('problem running xelatex, cannot generate form output') 1285 gmDispatcher.send(signal = 'statustext', msg = _('Error running xelatex. Cannot turn Xe(La)TeX template into PDF.'), beep = True) 1286 return None 1287 1288 sandboxed_pdf_name = u'%s.pdf' % os.path.splitext(sandboxed_instance_filename)[0] 1289 target_dir = os.path.split(self.instance_filename)[0] 1290 try: 1291 shutil.move(sandboxed_pdf_name, target_dir) 1292 except IOError: 1293 _log.exception('cannot move sandboxed PDF: %s -> %s', sandboxed_pdf_name, target_dir) 1294 gmDispatcher.send(signal = 'statustext', msg = _('Sandboxed PDF output file cannot be moved.'), beep = True) 1295 return None 1296 1297 final_pdf_name = u'%s.pdf' % os.path.splitext(self.instance_filename)[0] 1298 1299 try: 1300 open(final_pdf_name, 'r').close() 1301 except IOError: 1302 _log.exception('cannot open target PDF: %s', final_pdf_name) 1303 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True) 1304 return None 1305 1306 self.final_output_filenames = [final_pdf_name] 1307 1308 return final_pdf_name1316 """A forms engine wrapping Gnuplot.""" 1317 1318 #-------------------------------------------------------- 1322 #--------------------------------------------------------1367 #------------------------------------------------------------ 1368 form_engines[u'G'] = cGnuplotForm 1369 1370 #============================================================ 1371 # fPDF form engine 1372 #------------------------------------------------------------1324 """Allow editing the instance of the template.""" 1325 self.re_editable_filenames = [] 1326 return True1327 #--------------------------------------------------------1329 """Generate output suitable for further processing outside this class, e.g. printing. 1330 1331 Expects .data_filename to be set. 1332 """ 1333 self.conf_filename = gmTools.get_unique_filename(prefix = 'gm2gpl-', suffix = '.conf') 1334 conf_file = codecs.open(self.conf_filename, 'wb', 'utf8') 1335 conf_file.write('# setting the gnuplot data file\n') 1336 conf_file.write("gm2gpl_datafile = '%s'\n" % self.data_filename) 1337 conf_file.close() 1338 1339 # FIXME: cater for configurable path 1340 if platform.system() == 'Windows': 1341 exec_name = 'gnuplot.exe' 1342 else: 1343 exec_name = 'gnuplot' 1344 1345 args = [exec_name, '-p', self.conf_filename, self.template_filename] 1346 _log.debug('plotting args: %s' % str(args)) 1347 1348 try: 1349 gp = subprocess.Popen ( 1350 args = args, 1351 close_fds = True 1352 ) 1353 except (OSError, ValueError, subprocess.CalledProcessError): 1354 _log.exception('there was a problem executing gnuplot') 1355 gmDispatcher.send(signal = u'statustext', msg = _('Error running gnuplot. Cannot plot data.'), beep = True) 1356 return 1357 1358 gp.communicate() 1359 1360 self.final_output_filenames = [ 1361 self.conf_filename, 1362 self.data_filename, 1363 self.template_filename 1364 ] 1365 1366 return1374 """A forms engine wrapping PDF forms. 1375 1376 Johann Felix Soden <johfel@gmx.de> helped with this. 1377 1378 http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf 1379 1380 http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/fdf_data_exchange.pdf 1381 """ 13821579 #------------------------------------------------------------ 1580 form_engines[u'P'] = cPDFForm 1581 1582 #============================================================ 1583 # older code 1584 #------------------------------------------------------------1384 1385 super(cPDFForm, self).__init__(template_file = template_file) 1386 1387 # detect pdftk 1388 found, self.pdftk_binary = gmShellAPI.detect_external_binary(binary = r'pdftk') 1389 if not found: 1390 raise ImportError('<pdftk(.exe)> not found') 1391 return # should be superfluous, actually 1392 1393 enc = sys.getfilesystemencoding() 1394 self.pdftk_binary = self.pdftk_binary.encode(enc) 1395 1396 base_name, ext = os.path.splitext(self.template_filename) 1397 self.fdf_dumped_filename = (u'%s.fdf' % base_name).encode(enc) 1398 self.fdf_replaced_filename = (u'%s-replaced.fdf' % base_name).encode(enc) 1399 self.pdf_filled_filename = (u'%s-filled.pdf' % base_name).encode(enc) 1400 self.pdf_flattened_filename = (u'%s-filled-flattened.pdf' % base_name).encode(enc)1401 #--------------------------------------------------------1403 1404 # dump form fields from template 1405 cmd_line = [ 1406 self.pdftk_binary, 1407 self.template_filename, 1408 r'generate_fdf', 1409 r'output', 1410 self.fdf_dumped_filename 1411 ] 1412 _log.debug(u' '.join(cmd_line)) 1413 try: 1414 pdftk = subprocess.Popen(cmd_line) 1415 except OSError: 1416 _log.exception('cannot run <pdftk> (dump data from form)') 1417 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot extract fields from PDF form template.'), beep = True) 1418 return False 1419 1420 pdftk.communicate() 1421 if pdftk.returncode != 0: 1422 _log.error('<pdftk> returned [%s], failed to dump data from PDF form into FDF', pdftk.returncode) 1423 return False 1424 1425 # parse dumped FDF file for "/V (...)" records 1426 # and replace placeholders therein 1427 fdf_dumped_file = open(self.fdf_dumped_filename, 'rbU') 1428 fdf_replaced_file = codecs.open(self.fdf_replaced_filename, 'wb') 1429 1430 string_value_regex = r'\s*/V\s*\(.+\)\s*$' 1431 for line in fdf_dumped_file: 1432 if not regex.match(string_value_regex, line): 1433 fdf_replaced_file.write(line) 1434 continue 1435 1436 # strip cruft around the string value 1437 raw_str_val = line.strip() # remove framing whitespace 1438 raw_str_val = raw_str_val[2:] # remove leading "/V" 1439 raw_str_val = raw_str_val.lstrip() # remove whitespace between "/V" and "(" 1440 raw_str_val = raw_str_val[1:] # remove opening "(" 1441 raw_str_val = raw_str_val[2:] # remove BOM-16-BE 1442 raw_str_val = raw_str_val.rstrip() # remove trailing whitespace 1443 raw_str_val = raw_str_val[:-1] # remove closing ")" 1444 1445 # work on FDF escapes 1446 raw_str_val = raw_str_val.replace('\(', '(') # remove escaping of "(" 1447 raw_str_val = raw_str_val.replace('\)', ')') # remove escaping of ")" 1448 1449 # by now raw_str_val should contain the actual 1450 # string value, albeit encoded as UTF-16, so 1451 # decode it into a unicode object, 1452 # split multi-line fields on "\n" literal 1453 raw_str_lines = raw_str_val.split('\x00\\n') 1454 value_template_lines = [] 1455 for raw_str_line in raw_str_lines: 1456 value_template_lines.append(raw_str_line.decode('utf_16_be')) 1457 1458 replaced_lines = [] 1459 for value_template in value_template_lines: 1460 # find any placeholders within 1461 placeholders_in_value = regex.findall(data_source.placeholder_regex, value_template, regex.IGNORECASE) 1462 for placeholder in placeholders_in_value: 1463 try: 1464 replacement = data_source[placeholder] 1465 except: 1466 _log.exception(replacement) 1467 replacement = _('error with placeholder [%s]') % placeholder 1468 if replacement is None: 1469 replacement = _('error with placeholder [%s]') % placeholder 1470 value_template = value_template.replace(placeholder, replacement) 1471 1472 value_template = value_template.encode('utf_16_be') 1473 1474 if len(placeholders_in_value) > 0: 1475 value_template = value_template.replace(r'(', r'\(') 1476 value_template = value_template.replace(r')', r'\)') 1477 1478 replaced_lines.append(value_template) 1479 1480 replaced_line = '\x00\\n'.join(replaced_lines) 1481 1482 fdf_replaced_file.write('/V (') 1483 fdf_replaced_file.write(codecs.BOM_UTF16_BE) 1484 fdf_replaced_file.write(replaced_line) 1485 fdf_replaced_file.write(')\n') 1486 1487 fdf_replaced_file.close() 1488 fdf_dumped_file.close() 1489 1490 # merge replaced data back into form 1491 cmd_line = [ 1492 self.pdftk_binary, 1493 self.template_filename, 1494 r'fill_form', 1495 self.fdf_replaced_filename, 1496 r'output', 1497 self.pdf_filled_filename 1498 ] 1499 _log.debug(u' '.join(cmd_line)) 1500 try: 1501 pdftk = subprocess.Popen(cmd_line) 1502 except OSError: 1503 _log.exception('cannot run <pdftk> (merge data into form)') 1504 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot fill in PDF form template.'), beep = True) 1505 return False 1506 1507 pdftk.communicate() 1508 if pdftk.returncode != 0: 1509 _log.error('<pdftk> returned [%s], failed to merge FDF data into PDF form', pdftk.returncode) 1510 return False 1511 1512 return True1513 #--------------------------------------------------------1515 mimetypes = [ 1516 u'application/pdf', 1517 u'application/x-pdf' 1518 ] 1519 1520 for mimetype in mimetypes: 1521 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.pdf_filled_filename) 1522 if editor_cmd is not None: 1523 break 1524 1525 if editor_cmd is None: 1526 _log.debug('editor cmd not found, trying viewer cmd') 1527 for mimetype in mimetypes: 1528 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.pdf_filled_filename) 1529 if editor_cmd is not None: 1530 break 1531 1532 if editor_cmd is None: 1533 return False 1534 1535 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 1536 1537 path, fname = os.path.split(self.pdf_filled_filename) 1538 candidate = os.path.join(gmTools.gmPaths().home_dir, fname) 1539 1540 if os.access(candidate, os.R_OK): 1541 _log.debug('filled-in PDF found: %s', candidate) 1542 os.rename(self.pdf_filled_filename, self.pdf_filled_filename + '.bak') 1543 shutil.move(candidate, path) 1544 else: 1545 _log.debug('filled-in PDF not found: %s', candidate) 1546 1547 self.re_editable_filenames = [self.pdf_filled_filename] 1548 1549 return result1550 #--------------------------------------------------------1552 """Generate output suitable for further processing outside this class, e.g. printing.""" 1553 1554 # eventually flatten the filled in form so we 1555 # can keep both a flattened and an editable copy: 1556 cmd_line = [ 1557 self.pdftk_binary, 1558 self.pdf_filled_filename, 1559 r'output', 1560 self.pdf_flattened_filename, 1561 r'flatten' 1562 ] 1563 _log.debug(u' '.join(cmd_line)) 1564 try: 1565 pdftk = subprocess.Popen(cmd_line) 1566 except OSError: 1567 _log.exception('cannot run <pdftk> (flatten filled in form)') 1568 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot flatten filled in PDF form.'), beep = True) 1569 return None 1570 1571 pdftk.communicate() 1572 if pdftk.returncode != 0: 1573 _log.error('<pdftk> returned [%s], failed to flatten filled in PDF form', pdftk.returncode) 1574 return None 1575 1576 self.final_output_filenames = [self.pdf_flattened_filename] 1577 1578 return self.pdf_flattened_filename1586 """A forms engine wrapping LaTeX. 1587 """ 15911644 1645 1646 1647 1648 #================================================================ 1649 # define a class for HTML forms (for printing) 1650 #================================================================1593 try: 1594 latex = Cheetah.Template.Template (self.template, filter=LaTeXFilter, searchList=[params]) 1595 # create a 'sandbox' directory for LaTeX to play in 1596 self.tmp = tempfile.mktemp () 1597 os.makedirs (self.tmp) 1598 self.oldcwd = os.getcwd () 1599 os.chdir (self.tmp) 1600 stdin = os.popen ("latex", "w", 2048) 1601 stdin.write (str (latex)) #send text. LaTeX spits it's output into stdout 1602 # FIXME: send LaTeX output to the logger 1603 stdin.close () 1604 if not gmShellAPI.run_command_in_shell("dvips texput.dvi -o texput.ps", blocking=True): 1605 raise FormError ('DVIPS returned error') 1606 except EnvironmentError, e: 1607 _log.error(e.strerror) 1608 raise FormError (e.strerror) 1609 return file ("texput.ps")16101612 """ 1613 For testing purposes, runs Xdvi on the intermediate TeX output 1614 WARNING: don't try this on Windows 1615 """ 1616 gmShellAPI.run_command_in_shell("xdvi texput.dvi", blocking=True)16171619 if "%F" in command: 1620 command.replace ("%F", "texput.ps") 1621 else: 1622 command = "%s < texput.ps" % command 1623 try: 1624 if not gmShellAPI.run_command_in_shell(command, blocking=True): 1625 _log.error("external command %s returned non-zero" % command) 1626 raise FormError ('external command %s returned error' % command) 1627 except EnvironmentError, e: 1628 _log.error(e.strerror) 1629 raise FormError (e.strerror) 1630 return True16311633 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print') 1634 self.exe (command)16351652 """This class can create XML document from requested data, 1653 then process it with XSLT template and display results 1654 """ 1655 1656 # FIXME: make the path configurable ? 1657 _preview_program = u'oowriter ' #this program must be in the system PATH 16581735 1736 1737 #===================================================== 1738 #class LaTeXFilter(Cheetah.Filters.Filter):1660 1661 if template is None: 1662 raise ValueError(u'%s: cannot create form instance without a template' % __name__) 1663 1664 cFormEngine.__init__(self, template = template) 1665 1666 self._FormData = None 1667 1668 # here we know/can assume that the template was stored as a utf-8 1669 # encoded string so use that conversion to create unicode: 1670 #self._XSLTData = unicode(str(template.template_data), 'UTF-8') 1671 # but in fact, unicode() knows how to handle buffers, so simply: 1672 self._XSLTData = unicode(self.template.template_data, 'UTF-8', 'strict') 1673 1674 # we must still devise a method of extracting the SQL query: 1675 # - either by retrieving it from a particular tag in the XSLT or 1676 # - by making the stored template actually be a dict which, unpickled, 1677 # has the keys "xslt" and "sql" 1678 self._SQL_query = u'select 1' #this sql query must output valid xml1679 #-------------------------------------------------------- 1680 # external API 1681 #--------------------------------------------------------1683 """get data from backend and process it with XSLT template to produce readable output""" 1684 1685 # extract SQL (this is wrong but displays what is intended) 1686 xslt = libxml2.parseDoc(self._XSLTData) 1687 root = xslt.children 1688 for child in root: 1689 if child.type == 'element': 1690 self._SQL_query = child.content 1691 break 1692 1693 # retrieve data from backend 1694 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': self._SQL_query, 'args': sql_parameters}], get_col_idx = False) 1695 1696 __header = '<?xml version="1.0" encoding="UTF-8"?>\n' 1697 __body = rows[0][0] 1698 1699 # process XML data according to supplied XSLT, producing HTML 1700 self._XMLData =__header + __body 1701 style = libxslt.parseStylesheetDoc(xslt) 1702 xml = libxml2.parseDoc(self._XMLData) 1703 html = style.applyStylesheet(xml, None) 1704 self._FormData = html.serialize() 1705 1706 style.freeStylesheet() 1707 xml.freeDoc() 1708 html.freeDoc()1709 #--------------------------------------------------------1711 if self._FormData is None: 1712 raise ValueError, u'Preview request for empty form. Make sure the form is properly initialized and process() was performed' 1713 1714 fname = gmTools.get_unique_filename(prefix = u'gm_XSLT_form-', suffix = u'.html') 1715 #html_file = os.open(fname, 'wb') 1716 #html_file.write(self._FormData.encode('UTF-8')) 1717 html_file = codecs.open(fname, 'wb', 'utf8', 'strict') # or 'replace' ? 1718 html_file.write(self._FormData) 1719 html_file.close() 1720 1721 cmd = u'%s %s' % (self.__class__._preview_program, fname) 1722 1723 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = False): 1724 _log.error('%s: cannot launch report preview program' % __name__) 1725 return False 1726 1727 #os.unlink(self.filename) #delete file 1728 #FIXME: under Windows the temp file is deleted before preview program gets it (under Linux it works OK) 1729 1730 return True1731 #--------------------------------------------------------1778 1779 1780 #=========================================================== 1783 1784 #============================================================ 1785 # convenience functions 1786 #------------------------------------------------------------1741 """ 1742 Convience function to escape ISO-Latin-1 strings for TeX output 1743 WARNING: not all ISO-Latin-1 characters are expressible in TeX 1744 FIXME: nevertheless, there are a few more we could support 1745 1746 Also intelligently convert lists and tuples into TeX-style table lines 1747 """ 1748 if type (item) is types.UnicodeType or type (item) is types.StringType: 1749 item = item.replace ("\\", "\\backslash") # I wonder about this, do we want users to be able to use raw TeX? 1750 item = item.replace ("&", "\\&") 1751 item = item.replace ("$", "\\$") 1752 item = item.replace ('"', "") # okay, that's not right, but easiest solution for now 1753 item = item.replace ("\n", "\\\\ ") 1754 if len (item.strip ()) == 0: 1755 item = "\\relax " # sometimes TeX really hates empty strings, this seems to mollify it 1756 # FIXME: cover all of ISO-Latin-1 which can be expressed in TeX 1757 if type (item) is types.UnicodeType: 1758 item = item.encode ('latin-1', 'replace') 1759 trans = {'ß':'\\ss{}', 'ä': '\\"{a}', 'Ä' :'\\"{A}', 'ö': '\\"{o}', 'Ö': '\\"{O}', 'ü': '\\"{u}', 'Ü': '\\"{U}', 1760 '\x8a':'\\v{S}', '\x8a':'\\OE{}', '\x9a':'\\v{s}', '\x9c': '\\oe{}', '\a9f':'\\"{Y}', #Microsloth extensions 1761 '\x86': '{\\dag}', '\x87': '{\\ddag}', '\xa7':'{\\S}', '\xb6': '{\\P}', '\xa9': '{\\copyright}', '\xbf': '?`', 1762 '\xc0':'\\`{A}', '\xa1': "\\'{A}", '\xa2': '\\^{A}', '\xa3':'\\~{A}', '\\xc5': '{\AA}', 1763 '\xc7':'\\c{C}', '\xc8':'\\`{E}', 1764 '\xa1': '!`', 1765 '\xb5':'$\mu$', '\xa3': '\pounds{}', '\xa2':'cent'} 1766 for k, i in trans.items (): 1767 item = item.replace (k, i) 1768 elif type (item) is types.ListType or type (item) is types.TupleType: 1769 item = string.join ([self.filter (i, ' & ') for i in item], table_sep) 1770 elif item is None: 1771 item = '\\relax % Python None\n' 1772 elif type (item) is types.IntType or type (item) is types.FloatType: 1773 item = str (item) 1774 else: 1775 item = str (item) 1776 _log.warning("unknown type %s, string %s" % (type (item), item)) 1777 return item1788 """ 1789 Instantiates a FormEngine based on the form ID or name from the backend 1790 """ 1791 try: 1792 # it's a number: match to form ID 1793 id = int (id) 1794 cmd = 'select template, engine, pk from paperwork_templates where pk = %s' 1795 except ValueError: 1796 # it's a string, match to the form's name 1797 # FIXME: can we somehow OR like this: where name_short=%s OR name_long=%s ? 1798 cmd = 'select template, engine, flags, pk from paperwork_templates where name_short = %s' 1799 result = gmPG.run_ro_query ('reference', cmd, None, id) 1800 if result is None: 1801 _log.error('error getting form [%s]' % id) 1802 raise gmExceptions.FormError ('error getting form [%s]' % id) 1803 if len(result) == 0: 1804 _log.error('no form [%s] found' % id) 1805 raise gmExceptions.FormError ('no such form found [%s]' % id) 1806 if result[0][1] == 'L': 1807 return LaTeXForm (result[0][2], result[0][0]) 1808 elif result[0][1] == 'T': 1809 return TextForm (result[0][2], result[0][0]) 1810 else: 1811 _log.error('no form engine [%s] for form [%s]' % (result[0][1], id)) 1812 raise FormError ('no engine [%s] for form [%s]' % (result[0][1], id))1813 #------------------------------------------------------------- 1820 #------------------------------------------------------------- 1821 1822 test_letter = """ 1823 \\documentclass{letter} 1824 \\address{ $DOCTOR \\\\ 1825 $DOCTORADDRESS} 1826 \\signature{$DOCTOR} 1827 1828 \\begin{document} 1829 \\begin{letter}{$RECIPIENTNAME \\\\ 1830 $RECIPIENTADDRESS} 1831 1832 \\opening{Dear $RECIPIENTNAME} 1833 1834 \\textbf{Re:} $PATIENTNAME, DOB: $DOB, $PATIENTADDRESS \\\\ 1835 1836 $TEXT 1837 1838 \\ifnum$INCLUDEMEDS>0 1839 \\textbf{Medications List} 1840 1841 \\begin{tabular}{lll} 1842 $MEDSLIST 1843 \\end{tabular} 1844 \\fi 1845 1846 \\ifnum$INCLUDEDISEASES>0 1847 \\textbf{Disease List} 1848 1849 \\begin{tabular}{l} 1850 $DISEASELIST 1851 \\end{tabular} 1852 \\fi 1853 1854 \\closing{$CLOSING} 1855 1856 \\end{letter} 1857 \\end{document} 1858 """ 1859 18601862 f = open('../../test-area/ian/terry-form.tex') 1863 params = { 1864 'RECIPIENT': "Dr. R. Terry\n1 Main St\nNewcastle", 1865 'DOCTORSNAME': 'Ian Haywood', 1866 'DOCTORSADDRESS': '1 Smith St\nMelbourne', 1867 'PATIENTNAME':'Joe Bloggs', 1868 'PATIENTADDRESS':'18 Fred St\nMelbourne', 1869 'REQUEST':'echocardiogram', 1870 'THERAPY':'on warfarin', 1871 'CLINICALNOTES':"""heard new murmur 1872 Here's some 1873 crap to demonstrate how it can cover multiple lines.""", 1874 'COPYADDRESS':'Karsten Hilbert\nLeipzig, Germany', 1875 'ROUTINE':1, 1876 'URGENT':0, 1877 'FAX':1, 1878 'PHONE':1, 1879 'PENSIONER':1, 1880 'VETERAN':0, 1881 'PADS':0, 1882 'INSTRUCTIONS':u'Take the blue pill, Neo' 1883 } 1884 form = LaTeXForm (1, f.read()) 1885 form.process (params) 1886 form.xdvi () 1887 form.cleanup ()18881890 form = LaTeXForm (2, test_letter) 1891 params = {'RECIPIENTNAME':'Dr. Richard Terry', 1892 'RECIPIENTADDRESS':'1 Main St\nNewcastle', 1893 'DOCTOR':'Dr. Ian Haywood', 1894 'DOCTORADDRESS':'1 Smith St\nMelbourne', 1895 'PATIENTNAME':'Joe Bloggs', 1896 'PATIENTADDRESS':'18 Fred St, Melbourne', 1897 'TEXT':"""This is the main text of the referral letter""", 1898 'DOB':'12/3/65', 1899 'INCLUDEMEDS':1, 1900 'MEDSLIST':[["Amoxycillin", "500mg", "TDS"], ["Perindopril", "4mg", "OD"]], 1901 'INCLUDEDISEASES':0, 'DISEASELIST':'', 1902 'CLOSING':'Yours sincerely,' 1903 } 1904 form.process (params) 1905 print os.getcwd () 1906 form.xdvi () 1907 form.cleanup ()1908 #------------------------------------------------------------1910 template = open('../../test-area/ian/Formularkopf-DE.tex') 1911 form = LaTeXForm(template=template.read()) 1912 params = { 1913 'PATIENT LASTNAME': 'Kirk', 1914 'PATIENT FIRSTNAME': 'James T.', 1915 'PATIENT STREET': 'Hauptstrasse', 1916 'PATIENT ZIP': '02999', 1917 'PATIENT TOWN': 'Gross Saerchen', 1918 'PATIENT DOB': '22.03.1931' 1919 } 1920 form.process(params) 1921 form.xdvi() 1922 form.cleanup()1923 1924 #============================================================ 1925 # main 1926 #------------------------------------------------------------ 1927 if __name__ == '__main__': 1928 1929 if len(sys.argv) < 2: 1930 sys.exit() 1931 1932 if sys.argv[1] != 'test': 1933 sys.exit() 1934 1935 from Gnumed.pycommon import gmDateTime 1936 gmDateTime.init() 1937 1938 #-------------------------------------------------------- 1939 # OOo 1940 #--------------------------------------------------------1942 init_ooo()1943 #-------------------------------------------------------- 1948 #--------------------------------------------------------1950 srv = gmOOoConnector() 1951 doc = srv.open_document(filename = sys.argv[2]) 1952 print "document:", doc1953 #--------------------------------------------------------1955 doc = cOOoLetter(template_file = sys.argv[2]) 1956 doc.open_in_ooo() 1957 print "document:", doc 1958 raw_input('press <ENTER> to continue') 1959 doc.show() 1960 #doc.replace_placeholders() 1961 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1962 # doc = None 1963 # doc.close_in_ooo() 1964 raw_input('press <ENTER> to continue')1965 #--------------------------------------------------------1967 try: 1968 doc = open_uri_in_ooo(filename=sys.argv[1]) 1969 except: 1970 _log.exception('cannot open [%s] in OOo' % sys.argv[1]) 1971 raise 1972 1973 class myCloseListener(unohelper.Base, oooXCloseListener): 1974 def disposing(self, evt): 1975 print "disposing:"1976 def notifyClosing(self, evt): 1977 print "notifyClosing:" 1978 def queryClosing(self, evt, owner): 1979 # owner is True/False whether I am the owner of the doc 1980 print "queryClosing:" 1981 1982 l = myCloseListener() 1983 doc.addCloseListener(l) 1984 1985 tfs = doc.getTextFields().createEnumeration() 1986 print tfs 1987 print dir(tfs) 1988 while tfs.hasMoreElements(): 1989 tf = tfs.nextElement() 1990 if tf.supportsService('com.sun.star.text.TextField.JumpEdit'): 1991 print tf.getPropertyValue('PlaceHolder') 1992 print " ", tf.getPropertyValue('Hint') 1993 1994 # doc.close(True) # closes but leaves open the dedicated OOo window 1995 doc.dispose() # closes and disposes of the OOo window 1996 #--------------------------------------------------------1998 pat = gmPersonSearch.ask_for_patient() 1999 if pat is None: 2000 return 2001 gmPerson.set_active_patient(patient = pat) 2002 2003 doc = cOOoLetter(template_file = sys.argv[2]) 2004 doc.open_in_ooo() 2005 print doc 2006 doc.show() 2007 #doc.replace_placeholders() 2008 #doc.save_in_ooo('~/test_cOOoLetter.odt') 2009 doc = None 2010 # doc.close_in_ooo() 2011 raw_input('press <ENTER> to continue')2012 #-------------------------------------------------------- 2013 # other 2014 #--------------------------------------------------------2016 template = cFormTemplate(aPK_obj = sys.argv[2]) 2017 print template 2018 print template.export_to_file()2019 #--------------------------------------------------------2021 template = cFormTemplate(aPK_obj = sys.argv[2]) 2022 template.update_template_from_file(filename = sys.argv[3])2023 #--------------------------------------------------------2025 pat = gmPersonSearch.ask_for_patient() 2026 if pat is None: 2027 return 2028 gmPerson.set_active_patient(patient = pat) 2029 2030 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 2031 2032 path = os.path.abspath(sys.argv[2]) 2033 form = cLaTeXForm(template_file = path) 2034 2035 from Gnumed.wxpython import gmMacro 2036 ph = gmMacro.gmPlaceholderHandler() 2037 ph.debug = True 2038 instance_file = form.substitute_placeholders(data_source = ph) 2039 pdf_name = form.generate_output(instance_file = instance_file) 2040 print "final PDF file is:", pdf_name2041 #--------------------------------------------------------2043 pat = gmPersonSearch.ask_for_patient() 2044 if pat is None: 2045 return 2046 gmPerson.set_active_patient(patient = pat) 2047 2048 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 2049 2050 path = os.path.abspath(sys.argv[2]) 2051 form = cPDFForm(template_file = path) 2052 2053 from Gnumed.wxpython import gmMacro 2054 ph = gmMacro.gmPlaceholderHandler() 2055 ph.debug = True 2056 instance_file = form.substitute_placeholders(data_source = ph) 2057 pdf_name = form.generate_output(instance_file = instance_file) 2058 print "final PDF file is:", pdf_name2059 #--------------------------------------------------------2061 pat = gmPersonSearch.ask_for_patient() 2062 if pat is None: 2063 return 2064 gmPerson.set_active_patient(patient = pat) 2065 2066 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 2067 2068 path = os.path.abspath(sys.argv[2]) 2069 form = cAbiWordForm(template_file = path) 2070 2071 from Gnumed.wxpython import gmMacro 2072 ph = gmMacro.gmPlaceholderHandler() 2073 ph.debug = True 2074 instance_file = form.substitute_placeholders(data_source = ph) 2075 form.edit() 2076 final_name = form.generate_output(instance_file = instance_file) 2077 print "final file is:", final_name2078 #--------------------------------------------------------2080 pat = gmPersonSearch.ask_for_patient() 2081 if pat is None: 2082 return 2083 gmPerson.set_active_patient(patient = pat) 2084 2085 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 2086 2087 path = os.path.abspath(sys.argv[2]) 2088 form = cTextForm(template_file = path) 2089 2090 from Gnumed.wxpython import gmMacro 2091 ph = gmMacro.gmPlaceholderHandler() 2092 ph.debug = True 2093 print "placeholder substitution worked:", form.substitute_placeholders(data_source = ph) 2094 form.edit() 2095 form.generate_output()2096 #-------------------------------------------------------- 2097 #-------------------------------------------------------- 2098 #-------------------------------------------------------- 2099 # now run the tests 2100 #test_au() 2101 #test_de() 2102 2103 # OOo 2104 #test_init_ooo() 2105 #test_ooo_connect() 2106 #test_open_ooo_doc_from_srv() 2107 #test_open_ooo_doc_from_letter() 2108 #play_with_ooo() 2109 #test_cOOoLetter() 2110 2111 #test_cFormTemplate() 2112 #set_template_from_file() 2113 #test_latex_form() 2114 #test_pdf_form() 2115 #test_abiword_form() 2116 test_text_form() 2117 2118 #============================================================ 2119
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Mon Jan 7 03:56:38 2013 | http://epydoc.sourceforge.net |