Home | Trees | Indices | Help |
|
---|
|
1 # -*- coding: latin-1 -*- 2 """GNUmed forms classes 3 4 Business layer for printing all manners of forms, letters, scripts etc. 5 6 license: GPL v2 or later 7 """ 8 #============================================================ 9 __version__ = "$Revision: 1.79 $" 10 __author__ ="Ian Haywood <ihaywood@gnu.org>, karsten.hilbert@gmx.net" 11 12 13 import os, sys, time, os.path, logging 14 import codecs 15 import re as regex 16 import shutil 17 import random, platform, subprocess 18 import socket # needed for OOo on Windows 19 #, libxml2, libxslt 20 import shlex 21 22 23 if __name__ == '__main__': 24 sys.path.insert(0, '../../') 25 from Gnumed.pycommon import gmI18N 26 gmI18N.activate_locale() 27 gmI18N.install_domain(domain='gnumed') 28 from Gnumed.pycommon import gmTools 29 from Gnumed.pycommon import gmDispatcher 30 from Gnumed.pycommon import gmExceptions 31 from Gnumed.pycommon import gmMatchProvider 32 from Gnumed.pycommon import gmBorg 33 from Gnumed.pycommon import gmLog2 34 from Gnumed.pycommon import gmMimeLib 35 from Gnumed.pycommon import gmShellAPI 36 from Gnumed.pycommon import gmCfg 37 from Gnumed.pycommon import gmBusinessDBObject 38 from Gnumed.pycommon import gmPG2 39 40 from Gnumed.business import gmPerson 41 from Gnumed.business import gmStaff 42 from Gnumed.business import gmPersonSearch 43 from Gnumed.business import gmSurgery 44 45 46 _log = logging.getLogger('gm.forms') 47 _log.info(__version__) 48 49 #============================================================ 50 # this order is also used in choice boxes for the engine 51 form_engine_abbrevs = [u'O', u'L', u'I', u'G', u'P', u'A'] 52 53 form_engine_names = { 54 u'O': 'OpenOffice', 55 u'L': 'LaTeX', 56 u'I': 'Image editor', 57 u'G': 'Gnuplot script', 58 u'P': 'PDF forms', 59 u'A': 'AbiWord' 60 } 61 62 form_engine_template_wildcards = { 63 u'O': u'*.o?t', 64 u'L': u'*.tex', 65 u'G': u'*.gpl', 66 u'P': u'*.pdf', 67 u'A': u'*.abw' 68 } 69 70 # is filled in further below after each engine is defined 71 form_engines = {} 72 73 #============================================================ 74 # match providers 75 #============================================================7790 #============================================================79 80 query = u""" 81 SELECT 82 name_long AS data, 83 name_long AS list_label, 84 name_long AS field_label 85 FROM ref.v_paperwork_templates 86 WHERE name_long %(fragment_condition)s 87 ORDER BY list_label 88 """ 89 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])92105 #============================================================94 95 query = u""" 96 SELECT 97 name_short AS data, 98 name_short AS list_label, 99 name_short AS field_label 100 FROM ref.v_paperwork_templates 101 WHERE name_short %(fragment_condition)s 102 ORDER BY name_short 103 """ 104 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])107123 #============================================================109 110 query = u""" 111 SELECT DISTINCT ON (list_label) 112 pk AS data, 113 _(name) || ' (' || name || ')' AS list_label, 114 _(name) AS field_label 115 FROM ref.form_types 116 WHERE 117 _(name) %(fragment_condition)s 118 OR 119 name %(fragment_condition)s 120 ORDER BY list_label 121 """ 122 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])125 126 _cmd_fetch_payload = u'select * from ref.v_paperwork_templates where pk_paperwork_template = %s' 127 128 _cmds_store_payload = [ 129 u"""update ref.paperwork_templates set 130 name_short = %(name_short)s, 131 name_long = %(name_long)s, 132 fk_template_type = %(pk_template_type)s, 133 instance_type = %(instance_type)s, 134 engine = %(engine)s, 135 in_use = %(in_use)s, 136 filename = %(filename)s, 137 external_version = %(external_version)s 138 where 139 pk = %(pk_paperwork_template)s and 140 xmin = %(xmin_paperwork_template)s 141 """, 142 u"""select xmin_paperwork_template from ref.v_paperwork_templates where pk_paperwork_template = %(pk_paperwork_template)s""" 143 ] 144 145 _updatable_fields = [ 146 u'name_short', 147 u'name_long', 148 u'external_version', 149 u'pk_template_type', 150 u'instance_type', 151 u'engine', 152 u'in_use', 153 u'filename' 154 ] 155 156 _suffix4engine = { 157 u'O': u'.ott', 158 u'L': u'.tex', 159 u'T': u'.txt', 160 u'X': u'.xslt', 161 u'I': u'.img', 162 u'P': u'.pdf' 163 } 164 165 #--------------------------------------------------------233 #============================================================167 """The template itself better not be arbitrarily large unless you can handle that. 168 169 Note that the data type returned will be a buffer.""" 170 171 cmd = u'SELECT data FROM ref.paperwork_templates WHERE pk = %(pk)s' 172 rows, idx = gmPG2.run_ro_queries (queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}], get_col_idx = False) 173 174 if len(rows) == 0: 175 raise gmExceptions.NoSuchBusinessObjectError('cannot retrieve data for template pk = %s' % self.pk_obj) 176 177 return rows[0][0]178 179 template_data = property(_get_template_data, lambda x:x) 180 #--------------------------------------------------------182 """Export form template from database into file.""" 183 184 if filename is None: 185 if self._payload[self._idx['filename']] is None: 186 suffix = self.__class__._suffix4engine[self._payload[self._idx['engine']]] 187 else: 188 suffix = os.path.splitext(self._payload[self._idx['filename']].strip())[1].strip() 189 if suffix in [u'', u'.']: 190 suffix = self.__class__._suffix4engine[self._payload[self._idx['engine']]] 191 192 filename = gmTools.get_unique_filename ( 193 prefix = 'gm-%s-Template-' % self._payload[self._idx['engine']], 194 suffix = suffix 195 ) 196 197 data_query = { 198 'cmd': u'SELECT substring(data from %(start)s for %(size)s) FROM ref.paperwork_templates WHERE pk = %(pk)s', 199 'args': {'pk': self.pk_obj} 200 } 201 202 data_size_query = { 203 'cmd': u'select octet_length(data) from ref.paperwork_templates where pk = %(pk)s', 204 'args': {'pk': self.pk_obj} 205 } 206 207 result = gmPG2.bytea2file ( 208 data_query = data_query, 209 filename = filename, 210 data_size_query = data_size_query, 211 chunk_size = chunksize 212 ) 213 if result is False: 214 return None 215 216 return filename217 #--------------------------------------------------------219 gmPG2.file2bytea ( 220 filename = filename, 221 query = u'update ref.paperwork_templates set data = %(data)s::bytea where pk = %(pk)s and xmin = %(xmin)s', 222 args = {'pk': self.pk_obj, 'xmin': self._payload[self._idx['xmin_paperwork_template']]} 223 ) 224 # adjust for xmin change 225 self.refetch_payload()226 #--------------------------------------------------------228 fname = self.export_to_file() 229 engine = form_engines[self._payload[self._idx['engine']]] 230 form = engine(template_file = fname) 231 form.template = self 232 return form235 cmd = u'select pk from ref.paperwork_templates where name_long = %(lname)s and external_version = %(ver)s' 236 args = {'lname': name_long, 'ver': external_version} 237 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 238 239 if len(rows) == 0: 240 _log.error('cannot load form template [%s - %s]', name_long, external_version) 241 return None 242 243 return cFormTemplate(aPK_obj = rows[0]['pk'])244 #------------------------------------------------------------245 -def get_form_templates(engine=None, active_only=False, template_types=None, excluded_types=None):246 """Load form templates.""" 247 248 args = {'eng': engine, 'in_use': active_only} 249 where_parts = [u'1 = 1'] 250 251 if engine is not None: 252 where_parts.append(u'engine = %(eng)s') 253 254 if active_only: 255 where_parts.append(u'in_use IS true') 256 257 if template_types is not None: 258 args['incl_types'] = tuple(template_types) 259 where_parts.append(u'template_type IN %(incl_types)s') 260 261 if excluded_types is not None: 262 args['excl_types'] = tuple(excluded_types) 263 where_parts.append(u'template_type NOT IN %(excl_types)s') 264 265 cmd = u"SELECT * FROM ref.v_paperwork_templates WHERE %s ORDER BY in_use desc, name_long" % u'\nAND '.join(where_parts) 266 267 rows, idx = gmPG2.run_ro_queries ( 268 queries = [{'cmd': cmd, 'args': args}], 269 get_col_idx = True 270 ) 271 templates = [ cFormTemplate(row = {'pk_field': 'pk_paperwork_template', 'data': r, 'idx': idx}) for r in rows ] 272 273 return templates274 #------------------------------------------------------------276 277 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)' 278 rows, idx = gmPG2.run_rw_queries ( 279 queries = [ 280 {'cmd': cmd, 'args': {'type': template_type, 'nshort': name_short, 'nlong': name_long, 'ext_version': 'new'}}, 281 {'cmd': u"select currval(pg_get_serial_sequence('ref.paperwork_templates', 'pk'))"} 282 ], 283 return_data = True 284 ) 285 template = cFormTemplate(aPK_obj = rows[0][0]) 286 return template287 #------------------------------------------------------------289 rows, idx = gmPG2.run_rw_queries ( 290 queries = [ 291 {'cmd': u'delete from ref.paperwork_templates where pk=%(pk)s', 'args': {'pk': template['pk_paperwork_template']}} 292 ] 293 ) 294 return True295 #============================================================ 296 # OpenOffice/LibreOffice API 297 #============================================================ 298 uno = None 299 cOOoDocumentCloseListener = None 300 writer_binary = None 301 302 #-----------------------------------------------------------304 305 try: 306 which = subprocess.Popen ( 307 args = ('which', 'soffice'), 308 stdout = subprocess.PIPE, 309 stdin = subprocess.PIPE, 310 stderr = subprocess.PIPE, 311 universal_newlines = True 312 ) 313 except (OSError, ValueError, subprocess.CalledProcessError): 314 _log.exception('there was a problem executing [which soffice]') 315 return 316 317 soffice_path, err = which.communicate() 318 soffice_path = soffice_path.strip('\n') 319 uno_path = os.path.abspath ( os.path.join ( 320 os.path.dirname(os.path.realpath(soffice_path)), 321 '..', 322 'basis-link', 323 'program' 324 )) 325 326 _log.info('UNO should be at [%s], appending to sys.path', uno_path) 327 328 sys.path.append(uno_path)329 #-----------------------------------------------------------331 """FIXME: consider this: 332 333 try: 334 import uno 335 except: 336 print "This Script needs to be run with the python from OpenOffice.org" 337 print "Example: /opt/OpenOffice.org/program/python %s" % ( 338 os.path.basename(sys.argv[0])) 339 print "Or you need to insert the right path at the top, where uno.py is." 340 print "Default: %s" % default_path 341 """ 342 global uno 343 if uno is not None: 344 return 345 346 try: 347 import uno 348 except ImportError: 349 __configure_path_to_UNO() 350 import uno 351 352 global unohelper, oooXCloseListener, oooNoConnectException, oooPropertyValue 353 354 import unohelper 355 from com.sun.star.util import XCloseListener as oooXCloseListener 356 from com.sun.star.connection import NoConnectException as oooNoConnectException 357 from com.sun.star.beans import PropertyValue as oooPropertyValue 358 359 #---------------------------------- 360 class _cOOoDocumentCloseListener(unohelper.Base, oooXCloseListener): 361 """Listens for events sent by OOo during the document closing 362 sequence and notifies the GNUmed client GUI so it can 363 import the closed document into the database. 364 """ 365 def __init__(self, document=None): 366 self.document = document367 368 def queryClosing(self, evt, owner): 369 # owner is True/False whether I am the owner of the doc 370 pass 371 372 def notifyClosing(self, evt): 373 pass 374 375 def disposing(self, evt): 376 self.document.on_disposed_by_ooo() 377 self.document = None 378 #---------------------------------- 379 380 global cOOoDocumentCloseListener 381 cOOoDocumentCloseListener = _cOOoDocumentCloseListener 382 383 # search for writer binary 384 global writer_binary 385 found, binary = gmShellAPI.find_first_binary(binaries = [ 386 'lowriter', 387 'oowriter' 388 ]) 389 if found: 390 _log.debug('OOo/LO writer binary found: %s', binary) 391 writer_binary = binary 392 else: 393 _log.debug('OOo/LO writer binary NOT found') 394 raise ImportError('LibreOffice/OpenOffice (lowriter/oowriter) not found') 395 396 _log.debug('python UNO bridge successfully initialized') 397 398 #------------------------------------------------------------400 """This class handles the connection to OOo. 401 402 Its Singleton instance stays around once initialized. 403 """ 404 # FIXME: need to detect closure of OOo !524 #------------------------------------------------------------406 407 init_ooo() 408 409 self.__setup_connection_string() 410 411 self.resolver_uri = "com.sun.star.bridge.UnoUrlResolver" 412 self.desktop_uri = "com.sun.star.frame.Desktop" 413 414 self.max_connect_attempts = 5 415 416 self.local_context = uno.getComponentContext() 417 self.uri_resolver = self.local_context.ServiceManager.createInstanceWithContext(self.resolver_uri, self.local_context) 418 419 self.__desktop = None420 #-------------------------------------------------------- 421 # external API 422 #--------------------------------------------------------424 if self.__desktop is None: 425 _log.debug('no desktop, no cleanup') 426 return 427 428 try: 429 self.__desktop.terminate() 430 except: 431 _log.exception('cannot terminate OOo desktop')432 #--------------------------------------------------------434 """<filename> must be absolute""" 435 if self.desktop is None: 436 _log.error('cannot access OOo desktop') 437 return None 438 439 filename = os.path.expanduser(filename) 440 filename = os.path.abspath(filename) 441 document_uri = uno.systemPathToFileUrl(filename) 442 443 _log.debug('%s -> %s', filename, document_uri) 444 445 doc = self.desktop.loadComponentFromURL(document_uri, "_blank", 0, ()) 446 return doc447 #-------------------------------------------------------- 448 # internal helpers 449 #--------------------------------------------------------451 # later factor this out ! 452 dbcfg = gmCfg.cCfgSQL() 453 self.ooo_startup_settle_time = dbcfg.get2 ( 454 option = u'external.ooo.startup_settle_time', 455 workplace = gmSurgery.gmCurrentPractice().active_workplace, 456 bias = u'workplace', 457 default = 3.0 458 )459 #--------------------------------------------------------461 462 # socket: 463 # ooo_port = u'2002' 464 # #self.ooo_start_cmd = 'oowriter -invisible -norestore -nofirststartwizard -nologo -accept="socket,host=localhost,port=%s;urp;StarOffice.ServiceManager"' % ooo_port 465 # self.ooo_start_cmd = 'oowriter -invisible -norestore -accept="socket,host=localhost,port=%s;urp;"' % ooo_port 466 # self.remote_context_uri = "uno:socket,host=localhost,port=%s;urp;StarOffice.ComponentContext" % ooo_port 467 468 # pipe: 469 pipe_name = "uno-gm2lo-%s" % str(random.random())[2:] 470 _log.debug('expecting OOo/LO server on named pipe [%s]', pipe_name) 471 self.ooo_start_cmd = '%s --invisible --norestore --accept="pipe,name=%s;urp" &' % ( 472 writer_binary, 473 pipe_name 474 ) 475 _log.debug('startup command: %s', self.ooo_start_cmd) 476 477 self.remote_context_uri = "uno:pipe,name=%s;urp;StarOffice.ComponentContext" % pipe_name 478 _log.debug('remote context URI: %s', self.remote_context_uri)479 #--------------------------------------------------------481 _log.info('trying to start OOo server') 482 _log.debug('startup command: %s', self.ooo_start_cmd) 483 os.system(self.ooo_start_cmd) 484 self.__get_startup_settle_time() 485 _log.debug('waiting %s seconds for OOo to start up', self.ooo_startup_settle_time) 486 time.sleep(self.ooo_startup_settle_time)487 #-------------------------------------------------------- 488 # properties 489 #--------------------------------------------------------491 if self.__desktop is not None: 492 return self.__desktop 493 494 self.remote_context = None 495 496 attempts = self.max_connect_attempts 497 while attempts > 0: 498 499 _log.debug(u'attempt %s/%s', self.max_connect_attempts - attempts + 1, self.max_connect_attempts) 500 501 try: 502 self.remote_context = self.uri_resolver.resolve(self.remote_context_uri) 503 break 504 except oooNoConnectException: 505 _log.exception('cannot connect to OOo') 506 507 # first loop ? 508 if attempts == self.max_connect_attempts: 509 self.__startup_ooo() 510 else: 511 time.sleep(1) 512 513 attempts = attempts - 1 514 515 if self.remote_context is None: 516 raise OSError(-1, u'cannot connect to OpenOffice', self.remote_context_uri) 517 518 _log.debug('connection seems established') 519 self.__desktop = self.remote_context.ServiceManager.createInstanceWithContext(self.desktop_uri, self.remote_context) 520 _log.debug('got OOo desktop handle') 521 return self.__desktop522 523 desktop = property(_get_desktop, lambda x:x)526631 #-------------------------------------------------------- 632 # internal helpers 633 #-------------------------------------------------------- 634 635 #============================================================528 529 self.template_file = template_file 530 self.instance_type = instance_type 531 self.ooo_doc = None532 #-------------------------------------------------------- 533 # external API 534 #--------------------------------------------------------536 # connect to OOo 537 ooo_srv = gmOOoConnector() 538 539 # open doc in OOo 540 self.ooo_doc = ooo_srv.open_document(filename = self.template_file) 541 if self.ooo_doc is None: 542 _log.error('cannot open document in OOo') 543 return False 544 545 # listen for close events 546 pat = gmPerson.gmCurrentPatient() 547 pat.locked = True 548 listener = cOOoDocumentCloseListener(document = self) 549 self.ooo_doc.addCloseListener(listener) 550 551 return True552 #-------------------------------------------------------- 555 #--------------------------------------------------------557 558 # new style embedded, implicit placeholders 559 searcher = self.ooo_doc.createSearchDescriptor() 560 searcher.SearchCaseSensitive = False 561 searcher.SearchRegularExpression = True 562 searcher.SearchWords = True 563 searcher.SearchString = handler.placeholder_regex 564 565 placeholder_instance = self.ooo_doc.findFirst(searcher) 566 while placeholder_instance is not None: 567 try: 568 val = handler[placeholder_instance.String] 569 except: 570 val = _('error with placeholder [%s]') % placeholder_instance.String 571 _log.exception(val) 572 573 if val is None: 574 val = _('error with placeholder [%s]') % placeholder_instance.String 575 576 placeholder_instance.String = val 577 placeholder_instance = self.ooo_doc.findNext(placeholder_instance.End, searcher) 578 579 if not old_style_too: 580 return 581 582 # old style "explicit" placeholders 583 text_fields = self.ooo_doc.getTextFields().createEnumeration() 584 while text_fields.hasMoreElements(): 585 text_field = text_fields.nextElement() 586 587 # placeholder ? 588 if not text_field.supportsService('com.sun.star.text.TextField.JumpEdit'): 589 continue 590 # placeholder of type text ? 591 if text_field.PlaceHolderType != 0: 592 continue 593 594 replacement = handler[text_field.PlaceHolder] 595 if replacement is None: 596 continue 597 598 text_field.Anchor.setString(replacement)599 #--------------------------------------------------------601 if filename is not None: 602 target_url = uno.systemPathToFileUrl(os.path.abspath(os.path.expanduser(filename))) 603 save_args = ( 604 oooPropertyValue('Overwrite', 0, True, 0), 605 oooPropertyValue('FormatFilter', 0, 'swriter: StarOffice XML (Writer)', 0) 606 607 ) 608 # "store AS url" stores the doc, marks it unmodified and updates 609 # the internal media descriptor - as opposed to "store TO url" 610 self.ooo_doc.storeAsURL(target_url, save_args) 611 else: 612 self.ooo_doc.store()613 #--------------------------------------------------------615 self.ooo_doc.dispose() 616 pat = gmPerson.gmCurrentPatient() 617 pat.locked = False 618 self.ooo_doc = None619 #--------------------------------------------------------621 # get current file name from OOo, user may have used Save As 622 filename = uno.fileUrlToSystemPath(self.ooo_doc.URL) 623 # tell UI to import the file 624 gmDispatcher.send ( 625 signal = u'import_document_from_file', 626 filename = filename, 627 document_type = self.instance_type, 628 unlock_patient = True 629 ) 630 self.ooo_doc = None637 """Ancestor for forms.""" 638 642 #--------------------------------------------------------721 722 #================================================================ 723 # OOo template forms 724 #----------------------------------------------------------------644 """Parse the template into an instance and replace placeholders with values.""" 645 raise NotImplementedError646 #-------------------------------------------------------- 650 #--------------------------------------------------------652 """Generate output suitable for further processing outside this class, e.g. printing.""" 653 raise NotImplementedError654 #-------------------------------------------------------- 659 #--------------------------------------------------------661 """ 662 A sop to TeX which can't act as a true filter: to delete temporary files 663 """ 664 pass665 #--------------------------------------------------------667 """ 668 Executes the provided command. 669 If command cotains %F. it is substituted with the filename 670 Otherwise, the file is fed in on stdin 671 """ 672 pass673 #--------------------------------------------------------675 """Stores the parameters in the backend. 676 677 - link_obj can be a cursor, a connection or a service name 678 - assigning a cursor to link_obj allows the calling code to 679 group the call to store() into an enclosing transaction 680 (for an example see gmReferral.send_referral()...) 681 """ 682 # some forms may not have values ... 683 if params is None: 684 params = {} 685 patient_clinical = self.patient.get_emr() 686 encounter = patient_clinical.active_encounter['pk_encounter'] 687 # FIXME: get_active_episode is no more 688 #episode = patient_clinical.get_active_episode()['pk_episode'] 689 # generate "forever unique" name 690 cmd = "select name_short || ': <' || name_long || '::' || external_version || '>' from paperwork_templates where pk=%s"; 691 rows = gmPG.run_ro_query('reference', cmd, None, self.pk_def) 692 form_name = None 693 if rows is None: 694 _log.error('error retrieving form def for [%s]' % self.pk_def) 695 elif len(rows) == 0: 696 _log.error('no form def for [%s]' % self.pk_def) 697 else: 698 form_name = rows[0][0] 699 # we didn't get a name but want to store the form anyhow 700 if form_name is None: 701 form_name=time.time() # hopefully unique enough 702 # in one transaction 703 queries = [] 704 # - store form instance in form_instance 705 cmd = "insert into form_instances(fk_form_def, form_name, fk_episode, fk_encounter) values (%s, %s, %s, %s)" 706 queries.append((cmd, [self.pk_def, form_name, episode, encounter])) 707 # - store params in form_data 708 for key in params.keys(): 709 cmd = """ 710 insert into form_data(fk_instance, place_holder, value) 711 values ((select currval('form_instances_pk_seq')), %s, %s::text) 712 """ 713 queries.append((cmd, [key, params[key]])) 714 # - get inserted PK 715 queries.append(("select currval ('form_instances_pk_seq')", [])) 716 status, err = gmPG.run_commit('historica', queries, True) 717 if status is None: 718 _log.error('failed to store form [%s] (%s): %s' % (self.pk_def, form_name, err)) 719 return None 720 return status726 """A forms engine wrapping OOo.""" 727735 736 #================================================================ 737 # AbiWord template forms 738 #----------------------------------------------------------------729 super(self.__class__, self).__init__(template_file = template_file) 730 731 path, ext = os.path.splitext(self.template_filename) 732 if ext in [r'', r'.']: 733 ext = r'.odt' 734 self.instance_filename = r'%s-instance%s' % (path, ext)740 """A forms engine wrapping AbiWord.""" 741 742 placeholder_regex = r'\$<.+?>\$' 743814 #---------------------------------------------------------------- 815 form_engines[u'A'] = cAbiWordForm 816 817 #================================================================ 818 # LaTeX template forms 819 #----------------------------------------------------------------745 746 super(cAbiWordForm, self).__init__(template_file = template_file) 747 748 # detect abiword 749 found, self.abiword_binary = gmShellAPI.detect_external_binary(binary = r'abiword') 750 if not found: 751 raise ImportError('<abiword(.exe)> not found')752 #--------------------------------------------------------754 # should *actually* properly parse the XML 755 756 path, ext = os.path.splitext(self.template_filename) 757 if ext in [r'', r'.']: 758 ext = r'.abw' 759 self.instance_filename = r'%s-instance%s' % (path, ext) 760 761 template_file = codecs.open(self.template_filename, 'rU', 'utf8') 762 instance_file = codecs.open(self.instance_filename, 'wb', 'utf8') 763 764 if self.template is not None: 765 # inject placeholder values 766 data_source.set_placeholder(u'form_name_long', self.template['name_long']) 767 data_source.set_placeholder(u'form_name_short', self.template['name_short']) 768 data_source.set_placeholder(u'form_version', self.template['external_version']) 769 770 for line in template_file: 771 772 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 773 instance_file.write(line) 774 continue 775 776 # 1) find placeholders in this line 777 placeholders_in_line = regex.findall(cAbiWordForm.placeholder_regex, line, regex.IGNORECASE) 778 # 2) and replace them 779 for placeholder in placeholders_in_line: 780 try: 781 val = data_source[placeholder.replace(u'<', u'<').replace(u'>', u'>')] 782 except: 783 val = _('error with placeholder [%s]') % gmTools.xml_escape_string(placeholder) 784 _log.exception(val) 785 786 if val is None: 787 val = _('error with placeholder [%s]') % gmTools.xml_escape_string(placeholder) 788 789 line = line.replace(placeholder, val) 790 791 instance_file.write(line) 792 793 instance_file.close() 794 template_file.close() 795 796 if self.template is not None: 797 # remove temporary placeholders 798 data_source.unset_placeholder(u'form_name_long') 799 data_source.unset_placeholder(u'form_name_short') 800 data_source.unset_placeholder(u'form_version') 801 802 return803 #--------------------------------------------------------805 enc = sys.getfilesystemencoding() 806 cmd = (r'%s %s' % (self.abiword_binary, self.instance_filename.encode(enc))).encode(enc) 807 result = gmShellAPI.run_command_in_shell(command = cmd, blocking = True) 808 self.re_editable_filenames = [] 809 return result810 #--------------------------------------------------------821 """A forms engine wrapping LaTeX.""" 822977 #------------------------------------------------------------ 978 form_engines[u'L'] = cLaTeXForm 979 #============================================================ 980 # Gnuplot template forms 981 #------------------------------------------------------------824 super(self.__class__, self).__init__(template_file = template_file) 825 path, ext = os.path.splitext(self.template_filename) 826 if ext in [r'', r'.']: 827 ext = r'.tex' 828 self.instance_filename = r'%s-instance%s' % (path, ext)829 #--------------------------------------------------------831 832 template_file = codecs.open(self.template_filename, 'rU', 'utf8') 833 instance_file = codecs.open(self.instance_filename, 'wb', 'utf8') 834 835 if self.template is not None: 836 # inject placeholder values 837 data_source.set_placeholder(u'form_name_long', self.template['name_long']) 838 data_source.set_placeholder(u'form_name_short', self.template['name_short']) 839 data_source.set_placeholder(u'form_version', self.template['external_version']) 840 841 for line in template_file: 842 843 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 844 instance_file.write(line) 845 continue 846 847 # 1) find placeholders in this line 848 placeholders_in_line = regex.findall(data_source.placeholder_regex, line, regex.IGNORECASE) 849 # 2) and replace them 850 for placeholder in placeholders_in_line: 851 try: 852 val = data_source[placeholder] 853 except: 854 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder) 855 _log.exception(val) 856 857 if val is None: 858 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder) 859 860 line = line.replace(placeholder, val) 861 862 instance_file.write(line) 863 864 instance_file.close() 865 self.re_editable_filenames = [self.instance_filename] 866 template_file.close() 867 868 if self.template is not None: 869 # remove temporary placeholders 870 data_source.unset_placeholder(u'form_name_long') 871 data_source.unset_placeholder(u'form_name_short') 872 data_source.unset_placeholder(u'form_version') 873 874 return875 #--------------------------------------------------------877 878 mimetypes = [ 879 u'application/x-latex', 880 u'application/x-tex', 881 u'text/plain' 882 ] 883 884 for mimetype in mimetypes: 885 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename) 886 if editor_cmd is not None: 887 break 888 889 if editor_cmd is None: 890 # LaTeX code is text: also consider text *viewers* 891 # since pretty much any of them will be an editor as well 892 for mimetype in mimetypes: 893 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename) 894 if editor_cmd is not None: 895 break 896 897 # last resort 898 if editor_cmd is None: 899 if os.name == 'nt': 900 editor_cmd = u'notepad.exe %s' % self.instance_filename 901 else: 902 editor_cmd = u'sensible-editor %s' % self.instance_filename 903 904 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 905 self.re_editable_filenames = [self.instance_filename] 906 907 return result908 #--------------------------------------------------------910 911 if instance_file is None: 912 instance_file = self.instance_filename 913 914 try: 915 open(instance_file, 'r').close() 916 except: 917 _log.exception('cannot access form instance file [%s]', instance_file) 918 gmLog2.log_stack_trace() 919 return None 920 921 self.instance_filename = instance_file 922 923 _log.debug('ignoring <format> directive [%s], generating PDF', format) 924 925 # create sandbox for LaTeX to play in 926 sandbox_dir = os.path.splitext(self.template_filename)[0] 927 _log.debug('LaTeX sandbox directory: [%s]', sandbox_dir) 928 929 old_cwd = os.getcwd() 930 _log.debug('CWD: [%s]', old_cwd) 931 932 gmTools.mkdir(sandbox_dir) 933 934 os.chdir(sandbox_dir) 935 try: 936 sandboxed_instance_filename = os.path.join(sandbox_dir, os.path.split(self.instance_filename)[1]) 937 shutil.move(self.instance_filename, sandboxed_instance_filename) 938 self.re_editable_filenames = [sandboxed_instance_filename] 939 940 # LaTeX can need up to three runs to get cross references et al right 941 if platform.system() == 'Windows': 942 draft_cmd = r'pdflatex.exe -draftmode -interaction nonstopmode %s' % sandboxed_instance_filename 943 final_cmd = r'pdflatex.exe -interaction nonstopmode %s' % sandboxed_instance_filename 944 else: 945 draft_cmd = r'pdflatex -draftmode -interaction nonstopmode %s' % sandboxed_instance_filename 946 final_cmd = r'pdflatex -interaction nonstopmode %s' % sandboxed_instance_filename 947 for run_cmd in [draft_cmd, draft_cmd, final_cmd]: 948 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]): 949 _log.error('problem running pdflatex, cannot generate form output') 950 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdflatex. Cannot turn LaTeX template into PDF.'), beep = True) 951 os.chdir(old_cwd) 952 return None 953 finally: 954 os.chdir(old_cwd) 955 956 sandboxed_pdf_name = u'%s.pdf' % os.path.splitext(sandboxed_instance_filename)[0] 957 target_dir = os.path.split(self.instance_filename)[0] 958 try: 959 shutil.move(sandboxed_pdf_name, target_dir) 960 except IOError: 961 _log.exception('cannot move sandboxed PDF: %s -> %s', sandboxed_pdf_name, target_dir) 962 gmDispatcher.send(signal = 'statustext', msg = _('Sandboxed PDF output file cannot be moved.'), beep = True) 963 return None 964 965 final_pdf_name = u'%s.pdf' % os.path.splitext(self.instance_filename)[0] 966 967 try: 968 open(final_pdf_name, 'r').close() 969 except IOError: 970 _log.exception('cannot open target PDF: %s', final_pdf_name) 971 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True) 972 return None 973 974 self.final_output_filenames = [final_pdf_name] 975 976 return final_pdf_name983 """A forms engine wrapping Gnuplot.""" 984 985 #-------------------------------------------------------- 989 #--------------------------------------------------------1034 #------------------------------------------------------------ 1035 form_engines[u'G'] = cGnuplotForm 1036 1037 #============================================================ 1038 # fPDF form engine 1039 #------------------------------------------------------------991 """Allow editing the instance of the template.""" 992 self.re_editable_filenames = [] 993 return True994 #--------------------------------------------------------996 """Generate output suitable for further processing outside this class, e.g. printing. 997 998 Expects .data_filename to be set. 999 """ 1000 self.conf_filename = gmTools.get_unique_filename(prefix = 'gm2gpl-', suffix = '.conf') 1001 fname_file = codecs.open(self.conf_filename, 'wb', 'utf8') 1002 fname_file.write('# setting the gnuplot data file\n') 1003 fname_file.write("gm2gpl_datafile = '%s'\n" % self.data_filename) 1004 fname_file.close() 1005 1006 # FIXME: cater for configurable path 1007 if platform.system() == 'Windows': 1008 exec_name = 'gnuplot.exe' 1009 else: 1010 exec_name = 'gnuplot' 1011 1012 args = [exec_name, '-p', self.conf_filename, self.template_filename] 1013 _log.debug('plotting args: %s' % str(args)) 1014 1015 try: 1016 gp = subprocess.Popen ( 1017 args = args, 1018 close_fds = True 1019 ) 1020 except (OSError, ValueError, subprocess.CalledProcessError): 1021 _log.exception('there was a problem executing gnuplot') 1022 gmDispatcher.send(signal = u'statustext', msg = _('Error running gnuplot. Cannot plot data.'), beep = True) 1023 return 1024 1025 gp.communicate() 1026 1027 self.final_output_filenames = [ 1028 self.conf_filename, 1029 self.data_filename, 1030 self.template_filename 1031 ] 1032 1033 return1041 """A forms engine wrapping PDF forms. 1042 1043 Johann Felix Soden <johfel@gmx.de> helped with this. 1044 1045 http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf 1046 1047 http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/fdf_data_exchange.pdf 1048 """ 10491246 #------------------------------------------------------------ 1247 form_engines[u'P'] = cPDFForm 1248 1249 #============================================================ 1250 # older code 1251 #------------------------------------------------------------1051 1052 super(cPDFForm, self).__init__(template_file = template_file) 1053 1054 # detect pdftk 1055 found, self.pdftk_binary = gmShellAPI.detect_external_binary(binary = r'pdftk') 1056 if not found: 1057 raise ImportError('<pdftk(.exe)> not found') 1058 return # should be superfluous, actually 1059 1060 enc = sys.getfilesystemencoding() 1061 self.pdftk_binary = self.pdftk_binary.encode(enc) 1062 1063 base_name, ext = os.path.splitext(self.template_filename) 1064 self.fdf_dumped_filename = (u'%s.fdf' % base_name).encode(enc) 1065 self.fdf_replaced_filename = (u'%s-replaced.fdf' % base_name).encode(enc) 1066 self.pdf_filled_filename = (u'%s-filled.pdf' % base_name).encode(enc) 1067 self.pdf_flattened_filename = (u'%s-filled-flattened.pdf' % base_name).encode(enc)1068 #--------------------------------------------------------1070 1071 # dump form fields from template 1072 cmd_line = [ 1073 self.pdftk_binary, 1074 self.template_filename, 1075 r'generate_fdf', 1076 r'output', 1077 self.fdf_dumped_filename 1078 ] 1079 _log.debug(u' '.join(cmd_line)) 1080 try: 1081 pdftk = subprocess.Popen(cmd_line) 1082 except OSError: 1083 _log.exception('cannot run <pdftk> (dump data from form)') 1084 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot extract fields from PDF form template.'), beep = True) 1085 return False 1086 1087 pdftk.communicate() 1088 if pdftk.returncode != 0: 1089 _log.error('<pdftk> returned [%s], failed to dump data from PDF form into FDF', pdftk.returncode) 1090 return False 1091 1092 # parse dumped FDF file for "/V (...)" records 1093 # and replace placeholders therein 1094 fdf_dumped_file = open(self.fdf_dumped_filename, 'rbU') 1095 fdf_replaced_file = codecs.open(self.fdf_replaced_filename, 'wb') 1096 1097 string_value_regex = r'\s*/V\s*\(.+\)\s*$' 1098 for line in fdf_dumped_file: 1099 if not regex.match(string_value_regex, line): 1100 fdf_replaced_file.write(line) 1101 continue 1102 1103 # strip cruft around the string value 1104 raw_str_val = line.strip() # remove framing whitespace 1105 raw_str_val = raw_str_val[2:] # remove leading "/V" 1106 raw_str_val = raw_str_val.lstrip() # remove whitespace between "/V" and "(" 1107 raw_str_val = raw_str_val[1:] # remove opening "(" 1108 raw_str_val = raw_str_val[2:] # remove BOM-16-BE 1109 raw_str_val = raw_str_val.rstrip() # remove trailing whitespace 1110 raw_str_val = raw_str_val[:-1] # remove closing ")" 1111 1112 # work on FDF escapes 1113 raw_str_val = raw_str_val.replace('\(', '(') # remove escaping of "(" 1114 raw_str_val = raw_str_val.replace('\)', ')') # remove escaping of ")" 1115 1116 # by now raw_str_val should contain the actual 1117 # string value, albeit encoded as UTF-16, so 1118 # decode it into a unicode object, 1119 # split multi-line fields on "\n" literal 1120 raw_str_lines = raw_str_val.split('\x00\\n') 1121 value_template_lines = [] 1122 for raw_str_line in raw_str_lines: 1123 value_template_lines.append(raw_str_line.decode('utf_16_be')) 1124 1125 replaced_lines = [] 1126 for value_template in value_template_lines: 1127 # find any placeholders within 1128 placeholders_in_value = regex.findall(data_source.placeholder_regex, value_template, regex.IGNORECASE) 1129 for placeholder in placeholders_in_value: 1130 try: 1131 replacement = data_source[placeholder] 1132 except: 1133 _log.exception(replacement) 1134 replacement = _('error with placeholder [%s]') % placeholder 1135 if replacement is None: 1136 replacement = _('error with placeholder [%s]') % placeholder 1137 value_template = value_template.replace(placeholder, replacement) 1138 1139 value_template = value_template.encode('utf_16_be') 1140 1141 if len(placeholders_in_value) > 0: 1142 value_template = value_template.replace(r'(', r'\(') 1143 value_template = value_template.replace(r')', r'\)') 1144 1145 replaced_lines.append(value_template) 1146 1147 replaced_line = '\x00\\n'.join(replaced_lines) 1148 1149 fdf_replaced_file.write('/V (') 1150 fdf_replaced_file.write(codecs.BOM_UTF16_BE) 1151 fdf_replaced_file.write(replaced_line) 1152 fdf_replaced_file.write(')\n') 1153 1154 fdf_replaced_file.close() 1155 fdf_dumped_file.close() 1156 1157 # merge replaced data back into form 1158 cmd_line = [ 1159 self.pdftk_binary, 1160 self.template_filename, 1161 r'fill_form', 1162 self.fdf_replaced_filename, 1163 r'output', 1164 self.pdf_filled_filename 1165 ] 1166 _log.debug(u' '.join(cmd_line)) 1167 try: 1168 pdftk = subprocess.Popen(cmd_line) 1169 except OSError: 1170 _log.exception('cannot run <pdftk> (merge data into form)') 1171 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot fill in PDF form template.'), beep = True) 1172 return False 1173 1174 pdftk.communicate() 1175 if pdftk.returncode != 0: 1176 _log.error('<pdftk> returned [%s], failed to merge FDF data into PDF form', pdftk.returncode) 1177 return False 1178 1179 return True1180 #--------------------------------------------------------1182 mimetypes = [ 1183 u'application/pdf', 1184 u'application/x-pdf' 1185 ] 1186 1187 for mimetype in mimetypes: 1188 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.pdf_filled_filename) 1189 if editor_cmd is not None: 1190 break 1191 1192 if editor_cmd is None: 1193 _log.debug('editor cmd not found, trying viewer cmd') 1194 for mimetype in mimetypes: 1195 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.pdf_filled_filename) 1196 if editor_cmd is not None: 1197 break 1198 1199 if editor_cmd is None: 1200 return False 1201 1202 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 1203 1204 path, fname = os.path.split(self.pdf_filled_filename) 1205 candidate = os.path.join(gmTools.gmPaths().home_dir, fname) 1206 1207 if os.access(candidate, os.R_OK): 1208 _log.debug('filled-in PDF found: %s', candidate) 1209 os.rename(self.pdf_filled_filename, self.pdf_filled_filename + '.bak') 1210 shutil.move(candidate, path) 1211 else: 1212 _log.debug('filled-in PDF not found: %s', candidate) 1213 1214 self.re_editable_filenames = [self.pdf_filled_filename] 1215 1216 return result1217 #--------------------------------------------------------1219 """Generate output suitable for further processing outside this class, e.g. printing.""" 1220 1221 # eventually flatten the filled in form so we 1222 # can keep both a flattened and an editable copy: 1223 cmd_line = [ 1224 self.pdftk_binary, 1225 self.pdf_filled_filename, 1226 r'output', 1227 self.pdf_flattened_filename, 1228 r'flatten' 1229 ] 1230 _log.debug(u' '.join(cmd_line)) 1231 try: 1232 pdftk = subprocess.Popen(cmd_line) 1233 except OSError: 1234 _log.exception('cannot run <pdftk> (flatten filled in form)') 1235 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot flatten filled in PDF form.'), beep = True) 1236 return None 1237 1238 pdftk.communicate() 1239 if pdftk.returncode != 0: 1240 _log.error('<pdftk> returned [%s], failed to flatten filled in PDF form', pdftk.returncode) 1241 return None 1242 1243 self.final_output_filenames = [self.pdf_flattened_filename] 1244 1245 return self.pdf_flattened_filename1253 """A forms engine wrapping LaTeX. 1254 """ 12581311 1312 1313 1314 1315 #================================================================ 1316 # define a class for HTML forms (for printing) 1317 #================================================================1260 try: 1261 latex = Cheetah.Template.Template (self.template, filter=LaTeXFilter, searchList=[params]) 1262 # create a 'sandbox' directory for LaTeX to play in 1263 self.tmp = tempfile.mktemp () 1264 os.makedirs (self.tmp) 1265 self.oldcwd = os.getcwd () 1266 os.chdir (self.tmp) 1267 stdin = os.popen ("latex", "w", 2048) 1268 stdin.write (str (latex)) #send text. LaTeX spits it's output into stdout 1269 # FIXME: send LaTeX output to the logger 1270 stdin.close () 1271 if not gmShellAPI.run_command_in_shell("dvips texput.dvi -o texput.ps", blocking=True): 1272 raise FormError ('DVIPS returned error') 1273 except EnvironmentError, e: 1274 _log.error(e.strerror) 1275 raise FormError (e.strerror) 1276 return file ("texput.ps")12771279 """ 1280 For testing purposes, runs Xdvi on the intermediate TeX output 1281 WARNING: don't try this on Windows 1282 """ 1283 gmShellAPI.run_command_in_shell("xdvi texput.dvi", blocking=True)12841286 if "%F" in command: 1287 command.replace ("%F", "texput.ps") 1288 else: 1289 command = "%s < texput.ps" % command 1290 try: 1291 if not gmShellAPI.run_command_in_shell(command, blocking=True): 1292 _log.error("external command %s returned non-zero" % command) 1293 raise FormError ('external command %s returned error' % command) 1294 except EnvironmentError, e: 1295 _log.error(e.strerror) 1296 raise FormError (e.strerror) 1297 return True12981300 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print') 1301 self.exe (command)13021319 """This class can create XML document from requested data, 1320 then process it with XSLT template and display results 1321 """ 1322 1323 # FIXME: make the path configurable ? 1324 _preview_program = u'oowriter ' #this program must be in the system PATH 13251402 1403 1404 #===================================================== 1405 #class LaTeXFilter(Cheetah.Filters.Filter):1327 1328 if template is None: 1329 raise ValueError(u'%s: cannot create form instance without a template' % __name__) 1330 1331 cFormEngine.__init__(self, template = template) 1332 1333 self._FormData = None 1334 1335 # here we know/can assume that the template was stored as a utf-8 1336 # encoded string so use that conversion to create unicode: 1337 #self._XSLTData = unicode(str(template.template_data), 'UTF-8') 1338 # but in fact, unicode() knows how to handle buffers, so simply: 1339 self._XSLTData = unicode(self.template.template_data, 'UTF-8', 'strict') 1340 1341 # we must still devise a method of extracting the SQL query: 1342 # - either by retrieving it from a particular tag in the XSLT or 1343 # - by making the stored template actually be a dict which, unpickled, 1344 # has the keys "xslt" and "sql" 1345 self._SQL_query = u'select 1' #this sql query must output valid xml1346 #-------------------------------------------------------- 1347 # external API 1348 #--------------------------------------------------------1350 """get data from backend and process it with XSLT template to produce readable output""" 1351 1352 # extract SQL (this is wrong but displays what is intended) 1353 xslt = libxml2.parseDoc(self._XSLTData) 1354 root = xslt.children 1355 for child in root: 1356 if child.type == 'element': 1357 self._SQL_query = child.content 1358 break 1359 1360 # retrieve data from backend 1361 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': self._SQL_query, 'args': sql_parameters}], get_col_idx = False) 1362 1363 __header = '<?xml version="1.0" encoding="UTF-8"?>\n' 1364 __body = rows[0][0] 1365 1366 # process XML data according to supplied XSLT, producing HTML 1367 self._XMLData =__header + __body 1368 style = libxslt.parseStylesheetDoc(xslt) 1369 xml = libxml2.parseDoc(self._XMLData) 1370 html = style.applyStylesheet(xml, None) 1371 self._FormData = html.serialize() 1372 1373 style.freeStylesheet() 1374 xml.freeDoc() 1375 html.freeDoc()1376 #--------------------------------------------------------1378 if self._FormData is None: 1379 raise ValueError, u'Preview request for empty form. Make sure the form is properly initialized and process() was performed' 1380 1381 fname = gmTools.get_unique_filename(prefix = u'gm_XSLT_form-', suffix = u'.html') 1382 #html_file = os.open(fname, 'wb') 1383 #html_file.write(self._FormData.encode('UTF-8')) 1384 html_file = codecs.open(fname, 'wb', 'utf8', 'strict') # or 'replace' ? 1385 html_file.write(self._FormData) 1386 html_file.close() 1387 1388 cmd = u'%s %s' % (self.__class__._preview_program, fname) 1389 1390 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = False): 1391 _log.error('%s: cannot launch report preview program' % __name__) 1392 return False 1393 1394 #os.unlink(self.filename) #delete file 1395 #FIXME: under Windows the temp file is deleted before preview program gets it (under Linux it works OK) 1396 1397 return True1398 #--------------------------------------------------------1445 1446 1447 #=========================================================== 1450 1451 #============================================================ 1452 # convenience functions 1453 #------------------------------------------------------------1408 """ 1409 Convience function to escape ISO-Latin-1 strings for TeX output 1410 WARNING: not all ISO-Latin-1 characters are expressible in TeX 1411 FIXME: nevertheless, there are a few more we could support 1412 1413 Also intelligently convert lists and tuples into TeX-style table lines 1414 """ 1415 if type (item) is types.UnicodeType or type (item) is types.StringType: 1416 item = item.replace ("\\", "\\backslash") # I wonder about this, do we want users to be able to use raw TeX? 1417 item = item.replace ("&", "\\&") 1418 item = item.replace ("$", "\\$") 1419 item = item.replace ('"', "") # okay, that's not right, but easiest solution for now 1420 item = item.replace ("\n", "\\\\ ") 1421 if len (item.strip ()) == 0: 1422 item = "\\relax " # sometimes TeX really hates empty strings, this seems to mollify it 1423 # FIXME: cover all of ISO-Latin-1 which can be expressed in TeX 1424 if type (item) is types.UnicodeType: 1425 item = item.encode ('latin-1', 'replace') 1426 trans = {'ß':'\\ss{}', 'ä': '\\"{a}', 'Ä' :'\\"{A}', 'ö': '\\"{o}', 'Ö': '\\"{O}', 'ü': '\\"{u}', 'Ü': '\\"{U}', 1427 '\x8a':'\\v{S}', '\x8a':'\\OE{}', '\x9a':'\\v{s}', '\x9c': '\\oe{}', '\a9f':'\\"{Y}', #Microsloth extensions 1428 '\x86': '{\\dag}', '\x87': '{\\ddag}', '\xa7':'{\\S}', '\xb6': '{\\P}', '\xa9': '{\\copyright}', '\xbf': '?`', 1429 '\xc0':'\\`{A}', '\xa1': "\\'{A}", '\xa2': '\\^{A}', '\xa3':'\\~{A}', '\\xc5': '{\AA}', 1430 '\xc7':'\\c{C}', '\xc8':'\\`{E}', 1431 '\xa1': '!`', 1432 '\xb5':'$\mu$', '\xa3': '\pounds{}', '\xa2':'cent'} 1433 for k, i in trans.items (): 1434 item = item.replace (k, i) 1435 elif type (item) is types.ListType or type (item) is types.TupleType: 1436 item = string.join ([self.filter (i, ' & ') for i in item], table_sep) 1437 elif item is None: 1438 item = '\\relax % Python None\n' 1439 elif type (item) is types.IntType or type (item) is types.FloatType: 1440 item = str (item) 1441 else: 1442 item = str (item) 1443 _log.warning("unknown type %s, string %s" % (type (item), item)) 1444 return item1455 """ 1456 Instantiates a FormEngine based on the form ID or name from the backend 1457 """ 1458 try: 1459 # it's a number: match to form ID 1460 id = int (id) 1461 cmd = 'select template, engine, pk from paperwork_templates where pk = %s' 1462 except ValueError: 1463 # it's a string, match to the form's name 1464 # FIXME: can we somehow OR like this: where name_short=%s OR name_long=%s ? 1465 cmd = 'select template, engine, flags, pk from paperwork_templates where name_short = %s' 1466 result = gmPG.run_ro_query ('reference', cmd, None, id) 1467 if result is None: 1468 _log.error('error getting form [%s]' % id) 1469 raise gmExceptions.FormError ('error getting form [%s]' % id) 1470 if len(result) == 0: 1471 _log.error('no form [%s] found' % id) 1472 raise gmExceptions.FormError ('no such form found [%s]' % id) 1473 if result[0][1] == 'L': 1474 return LaTeXForm (result[0][2], result[0][0]) 1475 elif result[0][1] == 'T': 1476 return TextForm (result[0][2], result[0][0]) 1477 else: 1478 _log.error('no form engine [%s] for form [%s]' % (result[0][1], id)) 1479 raise FormError ('no engine [%s] for form [%s]' % (result[0][1], id))1480 #------------------------------------------------------------- 1487 #------------------------------------------------------------- 1488 1489 test_letter = """ 1490 \\documentclass{letter} 1491 \\address{ $DOCTOR \\\\ 1492 $DOCTORADDRESS} 1493 \\signature{$DOCTOR} 1494 1495 \\begin{document} 1496 \\begin{letter}{$RECIPIENTNAME \\\\ 1497 $RECIPIENTADDRESS} 1498 1499 \\opening{Dear $RECIPIENTNAME} 1500 1501 \\textbf{Re:} $PATIENTNAME, DOB: $DOB, $PATIENTADDRESS \\\\ 1502 1503 $TEXT 1504 1505 \\ifnum$INCLUDEMEDS>0 1506 \\textbf{Medications List} 1507 1508 \\begin{tabular}{lll} 1509 $MEDSLIST 1510 \\end{tabular} 1511 \\fi 1512 1513 \\ifnum$INCLUDEDISEASES>0 1514 \\textbf{Disease List} 1515 1516 \\begin{tabular}{l} 1517 $DISEASELIST 1518 \\end{tabular} 1519 \\fi 1520 1521 \\closing{$CLOSING} 1522 1523 \\end{letter} 1524 \\end{document} 1525 """ 1526 15271529 f = open('../../test-area/ian/terry-form.tex') 1530 params = { 1531 'RECIPIENT': "Dr. R. Terry\n1 Main St\nNewcastle", 1532 'DOCTORSNAME': 'Ian Haywood', 1533 'DOCTORSADDRESS': '1 Smith St\nMelbourne', 1534 'PATIENTNAME':'Joe Bloggs', 1535 'PATIENTADDRESS':'18 Fred St\nMelbourne', 1536 'REQUEST':'echocardiogram', 1537 'THERAPY':'on warfarin', 1538 'CLINICALNOTES':"""heard new murmur 1539 Here's some 1540 crap to demonstrate how it can cover multiple lines.""", 1541 'COPYADDRESS':'Karsten Hilbert\nLeipzig, Germany', 1542 'ROUTINE':1, 1543 'URGENT':0, 1544 'FAX':1, 1545 'PHONE':1, 1546 'PENSIONER':1, 1547 'VETERAN':0, 1548 'PADS':0, 1549 'INSTRUCTIONS':u'Take the blue pill, Neo' 1550 } 1551 form = LaTeXForm (1, f.read()) 1552 form.process (params) 1553 form.xdvi () 1554 form.cleanup ()15551557 form = LaTeXForm (2, test_letter) 1558 params = {'RECIPIENTNAME':'Dr. Richard Terry', 1559 'RECIPIENTADDRESS':'1 Main St\nNewcastle', 1560 'DOCTOR':'Dr. Ian Haywood', 1561 'DOCTORADDRESS':'1 Smith St\nMelbourne', 1562 'PATIENTNAME':'Joe Bloggs', 1563 'PATIENTADDRESS':'18 Fred St, Melbourne', 1564 'TEXT':"""This is the main text of the referral letter""", 1565 'DOB':'12/3/65', 1566 'INCLUDEMEDS':1, 1567 'MEDSLIST':[["Amoxycillin", "500mg", "TDS"], ["Perindopril", "4mg", "OD"]], 1568 'INCLUDEDISEASES':0, 'DISEASELIST':'', 1569 'CLOSING':'Yours sincerely,' 1570 } 1571 form.process (params) 1572 print os.getcwd () 1573 form.xdvi () 1574 form.cleanup ()1575 #------------------------------------------------------------1577 template = open('../../test-area/ian/Formularkopf-DE.tex') 1578 form = LaTeXForm(template=template.read()) 1579 params = { 1580 'PATIENT LASTNAME': 'Kirk', 1581 'PATIENT FIRSTNAME': 'James T.', 1582 'PATIENT STREET': 'Hauptstrasse', 1583 'PATIENT ZIP': '02999', 1584 'PATIENT TOWN': 'Gross Saerchen', 1585 'PATIENT DOB': '22.03.1931' 1586 } 1587 form.process(params) 1588 form.xdvi() 1589 form.cleanup()1590 1591 #============================================================ 1592 # main 1593 #------------------------------------------------------------ 1594 if __name__ == '__main__': 1595 1596 if len(sys.argv) < 2: 1597 sys.exit() 1598 1599 if sys.argv[1] != 'test': 1600 sys.exit() 1601 1602 from Gnumed.pycommon import gmDateTime 1603 gmDateTime.init() 1604 1605 #-------------------------------------------------------- 1606 # OOo 1607 #--------------------------------------------------------1609 init_ooo()1610 #-------------------------------------------------------- 1615 #--------------------------------------------------------1617 srv = gmOOoConnector() 1618 doc = srv.open_document(filename = sys.argv[2]) 1619 print "document:", doc1620 #--------------------------------------------------------1622 doc = cOOoLetter(template_file = sys.argv[2]) 1623 doc.open_in_ooo() 1624 print "document:", doc 1625 raw_input('press <ENTER> to continue') 1626 doc.show() 1627 #doc.replace_placeholders() 1628 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1629 # doc = None 1630 # doc.close_in_ooo() 1631 raw_input('press <ENTER> to continue')1632 #--------------------------------------------------------1634 try: 1635 doc = open_uri_in_ooo(filename=sys.argv[1]) 1636 except: 1637 _log.exception('cannot open [%s] in OOo' % sys.argv[1]) 1638 raise 1639 1640 class myCloseListener(unohelper.Base, oooXCloseListener): 1641 def disposing(self, evt): 1642 print "disposing:"1643 def notifyClosing(self, evt): 1644 print "notifyClosing:" 1645 def queryClosing(self, evt, owner): 1646 # owner is True/False whether I am the owner of the doc 1647 print "queryClosing:" 1648 1649 l = myCloseListener() 1650 doc.addCloseListener(l) 1651 1652 tfs = doc.getTextFields().createEnumeration() 1653 print tfs 1654 print dir(tfs) 1655 while tfs.hasMoreElements(): 1656 tf = tfs.nextElement() 1657 if tf.supportsService('com.sun.star.text.TextField.JumpEdit'): 1658 print tf.getPropertyValue('PlaceHolder') 1659 print " ", tf.getPropertyValue('Hint') 1660 1661 # doc.close(True) # closes but leaves open the dedicated OOo window 1662 doc.dispose() # closes and disposes of the OOo window 1663 #--------------------------------------------------------1665 pat = gmPersonSearch.ask_for_patient() 1666 if pat is None: 1667 return 1668 gmPerson.set_active_patient(patient = pat) 1669 1670 doc = cOOoLetter(template_file = sys.argv[2]) 1671 doc.open_in_ooo() 1672 print doc 1673 doc.show() 1674 #doc.replace_placeholders() 1675 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1676 doc = None 1677 # doc.close_in_ooo() 1678 raw_input('press <ENTER> to continue')1679 #-------------------------------------------------------- 1680 # other 1681 #--------------------------------------------------------1683 template = cFormTemplate(aPK_obj = sys.argv[2]) 1684 print template 1685 print template.export_to_file()1686 #--------------------------------------------------------1688 template = cFormTemplate(aPK_obj = sys.argv[2]) 1689 template.update_template_from_file(filename = sys.argv[3])1690 #--------------------------------------------------------1692 pat = gmPersonSearch.ask_for_patient() 1693 if pat is None: 1694 return 1695 gmPerson.set_active_patient(patient = pat) 1696 1697 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1698 1699 path = os.path.abspath(sys.argv[2]) 1700 form = cLaTeXForm(template_file = path) 1701 1702 from Gnumed.wxpython import gmMacro 1703 ph = gmMacro.gmPlaceholderHandler() 1704 ph.debug = True 1705 instance_file = form.substitute_placeholders(data_source = ph) 1706 pdf_name = form.generate_output(instance_file = instance_file) 1707 print "final PDF file is:", pdf_name1708 #--------------------------------------------------------1710 pat = gmPersonSearch.ask_for_patient() 1711 if pat is None: 1712 return 1713 gmPerson.set_active_patient(patient = pat) 1714 1715 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1716 1717 path = os.path.abspath(sys.argv[2]) 1718 form = cPDFForm(template_file = path) 1719 1720 from Gnumed.wxpython import gmMacro 1721 ph = gmMacro.gmPlaceholderHandler() 1722 ph.debug = True 1723 instance_file = form.substitute_placeholders(data_source = ph) 1724 pdf_name = form.generate_output(instance_file = instance_file) 1725 print "final PDF file is:", pdf_name1726 #--------------------------------------------------------1728 pat = gmPersonSearch.ask_for_patient() 1729 if pat is None: 1730 return 1731 gmPerson.set_active_patient(patient = pat) 1732 1733 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1734 1735 path = os.path.abspath(sys.argv[2]) 1736 form = cAbiWordForm(template_file = path) 1737 1738 from Gnumed.wxpython import gmMacro 1739 ph = gmMacro.gmPlaceholderHandler() 1740 ph.debug = True 1741 instance_file = form.substitute_placeholders(data_source = ph) 1742 form.edit() 1743 final_name = form.generate_output(instance_file = instance_file) 1744 print "final file is:", final_name1745 #-------------------------------------------------------- 1746 #-------------------------------------------------------- 1747 # now run the tests 1748 #test_au() 1749 #test_de() 1750 1751 # OOo 1752 #test_init_ooo() 1753 #test_ooo_connect() 1754 #test_open_ooo_doc_from_srv() 1755 #test_open_ooo_doc_from_letter() 1756 #play_with_ooo() 1757 #test_cOOoLetter() 1758 1759 #test_cFormTemplate() 1760 #set_template_from_file() 1761 #test_latex_form() 1762 #test_pdf_form() 1763 test_abiword_form() 1764 1765 #============================================================ 1766
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Tue Jul 31 03:58:58 2012 | http://epydoc.sourceforge.net |