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.""" 822976 #------------------------------------------------------------ 977 form_engines[u'L'] = cLaTeXForm 978 #============================================================ 979 # Gnuplot template forms 980 #------------------------------------------------------------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 939 # LaTeX can need up to three runs to get cross references et al right 940 if platform.system() == 'Windows': 941 draft_cmd = r'pdflatex.exe -draftmode -interaction nonstopmode %s' % sandboxed_instance_filename 942 final_cmd = r'pdflatex.exe -interaction nonstopmode %s' % sandboxed_instance_filename 943 else: 944 draft_cmd = r'pdflatex -draftmode -interaction nonstopmode %s' % sandboxed_instance_filename 945 final_cmd = r'pdflatex -interaction nonstopmode %s' % sandboxed_instance_filename 946 for run_cmd in [draft_cmd, draft_cmd, final_cmd]: 947 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]): 948 _log.error('problem running pdflatex, cannot generate form output') 949 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdflatex. Cannot turn LaTeX template into PDF.'), beep = True) 950 os.chdir(old_cwd) 951 return None 952 finally: 953 os.chdir(old_cwd) 954 955 sandboxed_pdf_name = u'%s.pdf' % os.path.splitext(sandboxed_instance_filename)[0] 956 target_dir = os.path.split(self.instance_filename)[0] 957 try: 958 shutil.move(sandboxed_pdf_name, target_dir) 959 except IOError: 960 _log.exception('cannot move sandboxed PDF: %s -> %s', sandboxed_pdf_name, target_dir) 961 gmDispatcher.send(signal = 'statustext', msg = _('Sandboxed PDF output file cannot be moved.'), beep = True) 962 return None 963 964 final_pdf_name = u'%s.pdf' % os.path.splitext(self.instance_filename)[0] 965 966 try: 967 open(final_pdf_name, 'r').close() 968 except IOError: 969 _log.exception('cannot open target PDF: %s', final_pdf_name) 970 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True) 971 return None 972 973 self.final_output_filenames = [final_pdf_name] 974 975 return final_pdf_name982 """A forms engine wrapping Gnuplot.""" 983 984 #-------------------------------------------------------- 988 #--------------------------------------------------------1033 #------------------------------------------------------------ 1034 form_engines[u'G'] = cGnuplotForm 1035 1036 #============================================================ 1037 # fPDF form engine 1038 #------------------------------------------------------------990 """Allow editing the instance of the template.""" 991 self.re_editable_filenames = [] 992 return True993 #--------------------------------------------------------995 """Generate output suitable for further processing outside this class, e.g. printing. 996 997 Expects .data_filename to be set. 998 """ 999 self.conf_filename = gmTools.get_unique_filename(prefix = 'gm2gpl-', suffix = '.conf') 1000 fname_file = codecs.open(self.conf_filename, 'wb', 'utf8') 1001 fname_file.write('# setting the gnuplot data file\n') 1002 fname_file.write("gm2gpl_datafile = '%s'\n" % self.data_filename) 1003 fname_file.close() 1004 1005 # FIXME: cater for configurable path 1006 if platform.system() == 'Windows': 1007 exec_name = 'gnuplot.exe' 1008 else: 1009 exec_name = 'gnuplot' 1010 1011 args = [exec_name, '-p', self.conf_filename, self.template_filename] 1012 _log.debug('plotting args: %s' % str(args)) 1013 1014 try: 1015 gp = subprocess.Popen ( 1016 args = args, 1017 close_fds = True 1018 ) 1019 except (OSError, ValueError, subprocess.CalledProcessError): 1020 _log.exception('there was a problem executing gnuplot') 1021 gmDispatcher.send(signal = u'statustext', msg = _('Error running gnuplot. Cannot plot data.'), beep = True) 1022 return 1023 1024 gp.communicate() 1025 1026 self.final_output_filenames = [ 1027 self.conf_filename, 1028 self.data_filename, 1029 self.template_filename 1030 ] 1031 1032 return1040 """A forms engine wrapping PDF forms. 1041 1042 Johann Felix Soden <johfel@gmx.de> helped with this. 1043 1044 http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf 1045 1046 http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/fdf_data_exchange.pdf 1047 """ 10481245 #------------------------------------------------------------ 1246 form_engines[u'P'] = cPDFForm 1247 1248 #============================================================ 1249 # older code 1250 #------------------------------------------------------------1050 1051 super(cPDFForm, self).__init__(template_file = template_file) 1052 1053 # detect pdftk 1054 found, self.pdftk_binary = gmShellAPI.detect_external_binary(binary = r'pdftk') 1055 if not found: 1056 raise ImportError('<pdftk(.exe)> not found') 1057 return # should be superfluous, actually 1058 1059 enc = sys.getfilesystemencoding() 1060 self.pdftk_binary = self.pdftk_binary.encode(enc) 1061 1062 base_name, ext = os.path.splitext(self.template_filename) 1063 self.fdf_dumped_filename = (u'%s.fdf' % base_name).encode(enc) 1064 self.fdf_replaced_filename = (u'%s-replaced.fdf' % base_name).encode(enc) 1065 self.pdf_filled_filename = (u'%s-filled.pdf' % base_name).encode(enc) 1066 self.pdf_flattened_filename = (u'%s-filled-flattened.pdf' % base_name).encode(enc)1067 #--------------------------------------------------------1069 1070 # dump form fields from template 1071 cmd_line = [ 1072 self.pdftk_binary, 1073 self.template_filename, 1074 r'generate_fdf', 1075 r'output', 1076 self.fdf_dumped_filename 1077 ] 1078 _log.debug(u' '.join(cmd_line)) 1079 try: 1080 pdftk = subprocess.Popen(cmd_line) 1081 except OSError: 1082 _log.exception('cannot run <pdftk> (dump data from form)') 1083 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot extract fields from PDF form template.'), beep = True) 1084 return False 1085 1086 pdftk.communicate() 1087 if pdftk.returncode != 0: 1088 _log.error('<pdftk> returned [%s], failed to dump data from PDF form into FDF', pdftk.returncode) 1089 return False 1090 1091 # parse dumped FDF file for "/V (...)" records 1092 # and replace placeholders therein 1093 fdf_dumped_file = open(self.fdf_dumped_filename, 'rbU') 1094 fdf_replaced_file = codecs.open(self.fdf_replaced_filename, 'wb') 1095 1096 string_value_regex = r'\s*/V\s*\(.+\)\s*$' 1097 for line in fdf_dumped_file: 1098 if not regex.match(string_value_regex, line): 1099 fdf_replaced_file.write(line) 1100 continue 1101 1102 # strip cruft around the string value 1103 raw_str_val = line.strip() # remove framing whitespace 1104 raw_str_val = raw_str_val[2:] # remove leading "/V" 1105 raw_str_val = raw_str_val.lstrip() # remove whitespace between "/V" and "(" 1106 raw_str_val = raw_str_val[1:] # remove opening "(" 1107 raw_str_val = raw_str_val[2:] # remove BOM-16-BE 1108 raw_str_val = raw_str_val.rstrip() # remove trailing whitespace 1109 raw_str_val = raw_str_val[:-1] # remove closing ")" 1110 1111 # work on FDF escapes 1112 raw_str_val = raw_str_val.replace('\(', '(') # remove escaping of "(" 1113 raw_str_val = raw_str_val.replace('\)', ')') # remove escaping of ")" 1114 1115 # by now raw_str_val should contain the actual 1116 # string value, albeit encoded as UTF-16, so 1117 # decode it into a unicode object, 1118 # split multi-line fields on "\n" literal 1119 raw_str_lines = raw_str_val.split('\x00\\n') 1120 value_template_lines = [] 1121 for raw_str_line in raw_str_lines: 1122 value_template_lines.append(raw_str_line.decode('utf_16_be')) 1123 1124 replaced_lines = [] 1125 for value_template in value_template_lines: 1126 # find any placeholders within 1127 placeholders_in_value = regex.findall(data_source.placeholder_regex, value_template, regex.IGNORECASE) 1128 for placeholder in placeholders_in_value: 1129 try: 1130 replacement = data_source[placeholder] 1131 except: 1132 _log.exception(replacement) 1133 replacement = _('error with placeholder [%s]') % placeholder 1134 if replacement is None: 1135 replacement = _('error with placeholder [%s]') % placeholder 1136 value_template = value_template.replace(placeholder, replacement) 1137 1138 value_template = value_template.encode('utf_16_be') 1139 1140 if len(placeholders_in_value) > 0: 1141 value_template = value_template.replace(r'(', r'\(') 1142 value_template = value_template.replace(r')', r'\)') 1143 1144 replaced_lines.append(value_template) 1145 1146 replaced_line = '\x00\\n'.join(replaced_lines) 1147 1148 fdf_replaced_file.write('/V (') 1149 fdf_replaced_file.write(codecs.BOM_UTF16_BE) 1150 fdf_replaced_file.write(replaced_line) 1151 fdf_replaced_file.write(')\n') 1152 1153 fdf_replaced_file.close() 1154 fdf_dumped_file.close() 1155 1156 # merge replaced data back into form 1157 cmd_line = [ 1158 self.pdftk_binary, 1159 self.template_filename, 1160 r'fill_form', 1161 self.fdf_replaced_filename, 1162 r'output', 1163 self.pdf_filled_filename 1164 ] 1165 _log.debug(u' '.join(cmd_line)) 1166 try: 1167 pdftk = subprocess.Popen(cmd_line) 1168 except OSError: 1169 _log.exception('cannot run <pdftk> (merge data into form)') 1170 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot fill in PDF form template.'), beep = True) 1171 return False 1172 1173 pdftk.communicate() 1174 if pdftk.returncode != 0: 1175 _log.error('<pdftk> returned [%s], failed to merge FDF data into PDF form', pdftk.returncode) 1176 return False 1177 1178 return True1179 #--------------------------------------------------------1181 mimetypes = [ 1182 u'application/pdf', 1183 u'application/x-pdf' 1184 ] 1185 1186 for mimetype in mimetypes: 1187 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.pdf_filled_filename) 1188 if editor_cmd is not None: 1189 break 1190 1191 if editor_cmd is None: 1192 _log.debug('editor cmd not found, trying viewer cmd') 1193 for mimetype in mimetypes: 1194 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.pdf_filled_filename) 1195 if editor_cmd is not None: 1196 break 1197 1198 if editor_cmd is None: 1199 return False 1200 1201 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 1202 1203 path, fname = os.path.split(self.pdf_filled_filename) 1204 candidate = os.path.join(gmTools.gmPaths().home_dir, fname) 1205 1206 if os.access(candidate, os.R_OK): 1207 _log.debug('filled-in PDF found: %s', candidate) 1208 os.rename(self.pdf_filled_filename, self.pdf_filled_filename + '.bak') 1209 shutil.move(candidate, path) 1210 else: 1211 _log.debug('filled-in PDF not found: %s', candidate) 1212 1213 self.re_editable_filenames = [self.pdf_filled_filename] 1214 1215 return result1216 #--------------------------------------------------------1218 """Generate output suitable for further processing outside this class, e.g. printing.""" 1219 1220 # eventually flatten the filled in form so we 1221 # can keep both a flattened and an editable copy: 1222 cmd_line = [ 1223 self.pdftk_binary, 1224 self.pdf_filled_filename, 1225 r'output', 1226 self.pdf_flattened_filename, 1227 r'flatten' 1228 ] 1229 _log.debug(u' '.join(cmd_line)) 1230 try: 1231 pdftk = subprocess.Popen(cmd_line) 1232 except OSError: 1233 _log.exception('cannot run <pdftk> (flatten filled in form)') 1234 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot flatten filled in PDF form.'), beep = True) 1235 return None 1236 1237 pdftk.communicate() 1238 if pdftk.returncode != 0: 1239 _log.error('<pdftk> returned [%s], failed to flatten filled in PDF form', pdftk.returncode) 1240 return None 1241 1242 self.final_output_filenames = [self.pdf_flattened_filename] 1243 1244 return self.pdf_flattened_filename1252 """A forms engine wrapping LaTeX. 1253 """ 12571310 1311 1312 1313 1314 #================================================================ 1315 # define a class for HTML forms (for printing) 1316 #================================================================1259 try: 1260 latex = Cheetah.Template.Template (self.template, filter=LaTeXFilter, searchList=[params]) 1261 # create a 'sandbox' directory for LaTeX to play in 1262 self.tmp = tempfile.mktemp () 1263 os.makedirs (self.tmp) 1264 self.oldcwd = os.getcwd () 1265 os.chdir (self.tmp) 1266 stdin = os.popen ("latex", "w", 2048) 1267 stdin.write (str (latex)) #send text. LaTeX spits it's output into stdout 1268 # FIXME: send LaTeX output to the logger 1269 stdin.close () 1270 if not gmShellAPI.run_command_in_shell("dvips texput.dvi -o texput.ps", blocking=True): 1271 raise FormError ('DVIPS returned error') 1272 except EnvironmentError, e: 1273 _log.error(e.strerror) 1274 raise FormError (e.strerror) 1275 return file ("texput.ps")12761278 """ 1279 For testing purposes, runs Xdvi on the intermediate TeX output 1280 WARNING: don't try this on Windows 1281 """ 1282 gmShellAPI.run_command_in_shell("xdvi texput.dvi", blocking=True)12831285 if "%F" in command: 1286 command.replace ("%F", "texput.ps") 1287 else: 1288 command = "%s < texput.ps" % command 1289 try: 1290 if not gmShellAPI.run_command_in_shell(command, blocking=True): 1291 _log.error("external command %s returned non-zero" % command) 1292 raise FormError ('external command %s returned error' % command) 1293 except EnvironmentError, e: 1294 _log.error(e.strerror) 1295 raise FormError (e.strerror) 1296 return True12971299 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print') 1300 self.exe (command)13011318 """This class can create XML document from requested data, 1319 then process it with XSLT template and display results 1320 """ 1321 1322 # FIXME: make the path configurable ? 1323 _preview_program = u'oowriter ' #this program must be in the system PATH 13241401 1402 1403 #===================================================== 1404 #class LaTeXFilter(Cheetah.Filters.Filter):1326 1327 if template is None: 1328 raise ValueError(u'%s: cannot create form instance without a template' % __name__) 1329 1330 cFormEngine.__init__(self, template = template) 1331 1332 self._FormData = None 1333 1334 # here we know/can assume that the template was stored as a utf-8 1335 # encoded string so use that conversion to create unicode: 1336 #self._XSLTData = unicode(str(template.template_data), 'UTF-8') 1337 # but in fact, unicode() knows how to handle buffers, so simply: 1338 self._XSLTData = unicode(self.template.template_data, 'UTF-8', 'strict') 1339 1340 # we must still devise a method of extracting the SQL query: 1341 # - either by retrieving it from a particular tag in the XSLT or 1342 # - by making the stored template actually be a dict which, unpickled, 1343 # has the keys "xslt" and "sql" 1344 self._SQL_query = u'select 1' #this sql query must output valid xml1345 #-------------------------------------------------------- 1346 # external API 1347 #--------------------------------------------------------1349 """get data from backend and process it with XSLT template to produce readable output""" 1350 1351 # extract SQL (this is wrong but displays what is intended) 1352 xslt = libxml2.parseDoc(self._XSLTData) 1353 root = xslt.children 1354 for child in root: 1355 if child.type == 'element': 1356 self._SQL_query = child.content 1357 break 1358 1359 # retrieve data from backend 1360 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': self._SQL_query, 'args': sql_parameters}], get_col_idx = False) 1361 1362 __header = '<?xml version="1.0" encoding="UTF-8"?>\n' 1363 __body = rows[0][0] 1364 1365 # process XML data according to supplied XSLT, producing HTML 1366 self._XMLData =__header + __body 1367 style = libxslt.parseStylesheetDoc(xslt) 1368 xml = libxml2.parseDoc(self._XMLData) 1369 html = style.applyStylesheet(xml, None) 1370 self._FormData = html.serialize() 1371 1372 style.freeStylesheet() 1373 xml.freeDoc() 1374 html.freeDoc()1375 #--------------------------------------------------------1377 if self._FormData is None: 1378 raise ValueError, u'Preview request for empty form. Make sure the form is properly initialized and process() was performed' 1379 1380 fname = gmTools.get_unique_filename(prefix = u'gm_XSLT_form-', suffix = u'.html') 1381 #html_file = os.open(fname, 'wb') 1382 #html_file.write(self._FormData.encode('UTF-8')) 1383 html_file = codecs.open(fname, 'wb', 'utf8', 'strict') # or 'replace' ? 1384 html_file.write(self._FormData) 1385 html_file.close() 1386 1387 cmd = u'%s %s' % (self.__class__._preview_program, fname) 1388 1389 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = False): 1390 _log.error('%s: cannot launch report preview program' % __name__) 1391 return False 1392 1393 #os.unlink(self.filename) #delete file 1394 #FIXME: under Windows the temp file is deleted before preview program gets it (under Linux it works OK) 1395 1396 return True1397 #--------------------------------------------------------1444 1445 1446 #=========================================================== 1449 1450 #============================================================ 1451 # convenience functions 1452 #------------------------------------------------------------1407 """ 1408 Convience function to escape ISO-Latin-1 strings for TeX output 1409 WARNING: not all ISO-Latin-1 characters are expressible in TeX 1410 FIXME: nevertheless, there are a few more we could support 1411 1412 Also intelligently convert lists and tuples into TeX-style table lines 1413 """ 1414 if type (item) is types.UnicodeType or type (item) is types.StringType: 1415 item = item.replace ("\\", "\\backslash") # I wonder about this, do we want users to be able to use raw TeX? 1416 item = item.replace ("&", "\\&") 1417 item = item.replace ("$", "\\$") 1418 item = item.replace ('"', "") # okay, that's not right, but easiest solution for now 1419 item = item.replace ("\n", "\\\\ ") 1420 if len (item.strip ()) == 0: 1421 item = "\\relax " # sometimes TeX really hates empty strings, this seems to mollify it 1422 # FIXME: cover all of ISO-Latin-1 which can be expressed in TeX 1423 if type (item) is types.UnicodeType: 1424 item = item.encode ('latin-1', 'replace') 1425 trans = {'ß':'\\ss{}', 'ä': '\\"{a}', 'Ä' :'\\"{A}', 'ö': '\\"{o}', 'Ö': '\\"{O}', 'ü': '\\"{u}', 'Ü': '\\"{U}', 1426 '\x8a':'\\v{S}', '\x8a':'\\OE{}', '\x9a':'\\v{s}', '\x9c': '\\oe{}', '\a9f':'\\"{Y}', #Microsloth extensions 1427 '\x86': '{\\dag}', '\x87': '{\\ddag}', '\xa7':'{\\S}', '\xb6': '{\\P}', '\xa9': '{\\copyright}', '\xbf': '?`', 1428 '\xc0':'\\`{A}', '\xa1': "\\'{A}", '\xa2': '\\^{A}', '\xa3':'\\~{A}', '\\xc5': '{\AA}', 1429 '\xc7':'\\c{C}', '\xc8':'\\`{E}', 1430 '\xa1': '!`', 1431 '\xb5':'$\mu$', '\xa3': '\pounds{}', '\xa2':'cent'} 1432 for k, i in trans.items (): 1433 item = item.replace (k, i) 1434 elif type (item) is types.ListType or type (item) is types.TupleType: 1435 item = string.join ([self.filter (i, ' & ') for i in item], table_sep) 1436 elif item is None: 1437 item = '\\relax % Python None\n' 1438 elif type (item) is types.IntType or type (item) is types.FloatType: 1439 item = str (item) 1440 else: 1441 item = str (item) 1442 _log.warning("unknown type %s, string %s" % (type (item), item)) 1443 return item1454 """ 1455 Instantiates a FormEngine based on the form ID or name from the backend 1456 """ 1457 try: 1458 # it's a number: match to form ID 1459 id = int (id) 1460 cmd = 'select template, engine, pk from paperwork_templates where pk = %s' 1461 except ValueError: 1462 # it's a string, match to the form's name 1463 # FIXME: can we somehow OR like this: where name_short=%s OR name_long=%s ? 1464 cmd = 'select template, engine, flags, pk from paperwork_templates where name_short = %s' 1465 result = gmPG.run_ro_query ('reference', cmd, None, id) 1466 if result is None: 1467 _log.error('error getting form [%s]' % id) 1468 raise gmExceptions.FormError ('error getting form [%s]' % id) 1469 if len(result) == 0: 1470 _log.error('no form [%s] found' % id) 1471 raise gmExceptions.FormError ('no such form found [%s]' % id) 1472 if result[0][1] == 'L': 1473 return LaTeXForm (result[0][2], result[0][0]) 1474 elif result[0][1] == 'T': 1475 return TextForm (result[0][2], result[0][0]) 1476 else: 1477 _log.error('no form engine [%s] for form [%s]' % (result[0][1], id)) 1478 raise FormError ('no engine [%s] for form [%s]' % (result[0][1], id))1479 #------------------------------------------------------------- 1486 #------------------------------------------------------------- 1487 1488 test_letter = """ 1489 \\documentclass{letter} 1490 \\address{ $DOCTOR \\\\ 1491 $DOCTORADDRESS} 1492 \\signature{$DOCTOR} 1493 1494 \\begin{document} 1495 \\begin{letter}{$RECIPIENTNAME \\\\ 1496 $RECIPIENTADDRESS} 1497 1498 \\opening{Dear $RECIPIENTNAME} 1499 1500 \\textbf{Re:} $PATIENTNAME, DOB: $DOB, $PATIENTADDRESS \\\\ 1501 1502 $TEXT 1503 1504 \\ifnum$INCLUDEMEDS>0 1505 \\textbf{Medications List} 1506 1507 \\begin{tabular}{lll} 1508 $MEDSLIST 1509 \\end{tabular} 1510 \\fi 1511 1512 \\ifnum$INCLUDEDISEASES>0 1513 \\textbf{Disease List} 1514 1515 \\begin{tabular}{l} 1516 $DISEASELIST 1517 \\end{tabular} 1518 \\fi 1519 1520 \\closing{$CLOSING} 1521 1522 \\end{letter} 1523 \\end{document} 1524 """ 1525 15261528 f = open('../../test-area/ian/terry-form.tex') 1529 params = { 1530 'RECIPIENT': "Dr. R. Terry\n1 Main St\nNewcastle", 1531 'DOCTORSNAME': 'Ian Haywood', 1532 'DOCTORSADDRESS': '1 Smith St\nMelbourne', 1533 'PATIENTNAME':'Joe Bloggs', 1534 'PATIENTADDRESS':'18 Fred St\nMelbourne', 1535 'REQUEST':'echocardiogram', 1536 'THERAPY':'on warfarin', 1537 'CLINICALNOTES':"""heard new murmur 1538 Here's some 1539 crap to demonstrate how it can cover multiple lines.""", 1540 'COPYADDRESS':'Karsten Hilbert\nLeipzig, Germany', 1541 'ROUTINE':1, 1542 'URGENT':0, 1543 'FAX':1, 1544 'PHONE':1, 1545 'PENSIONER':1, 1546 'VETERAN':0, 1547 'PADS':0, 1548 'INSTRUCTIONS':u'Take the blue pill, Neo' 1549 } 1550 form = LaTeXForm (1, f.read()) 1551 form.process (params) 1552 form.xdvi () 1553 form.cleanup ()15541556 form = LaTeXForm (2, test_letter) 1557 params = {'RECIPIENTNAME':'Dr. Richard Terry', 1558 'RECIPIENTADDRESS':'1 Main St\nNewcastle', 1559 'DOCTOR':'Dr. Ian Haywood', 1560 'DOCTORADDRESS':'1 Smith St\nMelbourne', 1561 'PATIENTNAME':'Joe Bloggs', 1562 'PATIENTADDRESS':'18 Fred St, Melbourne', 1563 'TEXT':"""This is the main text of the referral letter""", 1564 'DOB':'12/3/65', 1565 'INCLUDEMEDS':1, 1566 'MEDSLIST':[["Amoxycillin", "500mg", "TDS"], ["Perindopril", "4mg", "OD"]], 1567 'INCLUDEDISEASES':0, 'DISEASELIST':'', 1568 'CLOSING':'Yours sincerely,' 1569 } 1570 form.process (params) 1571 print os.getcwd () 1572 form.xdvi () 1573 form.cleanup ()1574 #------------------------------------------------------------1576 template = open('../../test-area/ian/Formularkopf-DE.tex') 1577 form = LaTeXForm(template=template.read()) 1578 params = { 1579 'PATIENT LASTNAME': 'Kirk', 1580 'PATIENT FIRSTNAME': 'James T.', 1581 'PATIENT STREET': 'Hauptstrasse', 1582 'PATIENT ZIP': '02999', 1583 'PATIENT TOWN': 'Gross Saerchen', 1584 'PATIENT DOB': '22.03.1931' 1585 } 1586 form.process(params) 1587 form.xdvi() 1588 form.cleanup()1589 1590 #============================================================ 1591 # main 1592 #------------------------------------------------------------ 1593 if __name__ == '__main__': 1594 1595 if len(sys.argv) < 2: 1596 sys.exit() 1597 1598 if sys.argv[1] != 'test': 1599 sys.exit() 1600 1601 from Gnumed.pycommon import gmDateTime 1602 gmDateTime.init() 1603 1604 #-------------------------------------------------------- 1605 # OOo 1606 #--------------------------------------------------------1608 init_ooo()1609 #-------------------------------------------------------- 1614 #--------------------------------------------------------1616 srv = gmOOoConnector() 1617 doc = srv.open_document(filename = sys.argv[2]) 1618 print "document:", doc1619 #--------------------------------------------------------1621 doc = cOOoLetter(template_file = sys.argv[2]) 1622 doc.open_in_ooo() 1623 print "document:", doc 1624 raw_input('press <ENTER> to continue') 1625 doc.show() 1626 #doc.replace_placeholders() 1627 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1628 # doc = None 1629 # doc.close_in_ooo() 1630 raw_input('press <ENTER> to continue')1631 #--------------------------------------------------------1633 try: 1634 doc = open_uri_in_ooo(filename=sys.argv[1]) 1635 except: 1636 _log.exception('cannot open [%s] in OOo' % sys.argv[1]) 1637 raise 1638 1639 class myCloseListener(unohelper.Base, oooXCloseListener): 1640 def disposing(self, evt): 1641 print "disposing:"1642 def notifyClosing(self, evt): 1643 print "notifyClosing:" 1644 def queryClosing(self, evt, owner): 1645 # owner is True/False whether I am the owner of the doc 1646 print "queryClosing:" 1647 1648 l = myCloseListener() 1649 doc.addCloseListener(l) 1650 1651 tfs = doc.getTextFields().createEnumeration() 1652 print tfs 1653 print dir(tfs) 1654 while tfs.hasMoreElements(): 1655 tf = tfs.nextElement() 1656 if tf.supportsService('com.sun.star.text.TextField.JumpEdit'): 1657 print tf.getPropertyValue('PlaceHolder') 1658 print " ", tf.getPropertyValue('Hint') 1659 1660 # doc.close(True) # closes but leaves open the dedicated OOo window 1661 doc.dispose() # closes and disposes of the OOo window 1662 #--------------------------------------------------------1664 pat = gmPersonSearch.ask_for_patient() 1665 if pat is None: 1666 return 1667 gmPerson.set_active_patient(patient = pat) 1668 1669 doc = cOOoLetter(template_file = sys.argv[2]) 1670 doc.open_in_ooo() 1671 print doc 1672 doc.show() 1673 #doc.replace_placeholders() 1674 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1675 doc = None 1676 # doc.close_in_ooo() 1677 raw_input('press <ENTER> to continue')1678 #-------------------------------------------------------- 1679 # other 1680 #--------------------------------------------------------1682 template = cFormTemplate(aPK_obj = sys.argv[2]) 1683 print template 1684 print template.export_to_file()1685 #--------------------------------------------------------1687 template = cFormTemplate(aPK_obj = sys.argv[2]) 1688 template.update_template_from_file(filename = sys.argv[3])1689 #--------------------------------------------------------1691 pat = gmPersonSearch.ask_for_patient() 1692 if pat is None: 1693 return 1694 gmPerson.set_active_patient(patient = pat) 1695 1696 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1697 1698 path = os.path.abspath(sys.argv[2]) 1699 form = cLaTeXForm(template_file = path) 1700 1701 from Gnumed.wxpython import gmMacro 1702 ph = gmMacro.gmPlaceholderHandler() 1703 ph.debug = True 1704 instance_file = form.substitute_placeholders(data_source = ph) 1705 pdf_name = form.generate_output(instance_file = instance_file) 1706 print "final PDF file is:", pdf_name1707 #--------------------------------------------------------1709 pat = gmPersonSearch.ask_for_patient() 1710 if pat is None: 1711 return 1712 gmPerson.set_active_patient(patient = pat) 1713 1714 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1715 1716 path = os.path.abspath(sys.argv[2]) 1717 form = cPDFForm(template_file = path) 1718 1719 from Gnumed.wxpython import gmMacro 1720 ph = gmMacro.gmPlaceholderHandler() 1721 ph.debug = True 1722 instance_file = form.substitute_placeholders(data_source = ph) 1723 pdf_name = form.generate_output(instance_file = instance_file) 1724 print "final PDF file is:", pdf_name1725 #--------------------------------------------------------1727 pat = gmPersonSearch.ask_for_patient() 1728 if pat is None: 1729 return 1730 gmPerson.set_active_patient(patient = pat) 1731 1732 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1733 1734 path = os.path.abspath(sys.argv[2]) 1735 form = cAbiWordForm(template_file = path) 1736 1737 from Gnumed.wxpython import gmMacro 1738 ph = gmMacro.gmPlaceholderHandler() 1739 ph.debug = True 1740 instance_file = form.substitute_placeholders(data_source = ph) 1741 form.edit() 1742 final_name = form.generate_output(instance_file = instance_file) 1743 print "final file is:", final_name1744 #-------------------------------------------------------- 1745 #-------------------------------------------------------- 1746 # now run the tests 1747 #test_au() 1748 #test_de() 1749 1750 # OOo 1751 #test_init_ooo() 1752 #test_ooo_connect() 1753 #test_open_ooo_doc_from_srv() 1754 #test_open_ooo_doc_from_letter() 1755 #play_with_ooo() 1756 #test_cOOoLetter() 1757 1758 #test_cFormTemplate() 1759 #set_template_from_file() 1760 #test_latex_form() 1761 #test_pdf_form() 1762 test_abiword_form() 1763 1764 #============================================================ 1765
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Mon Jun 25 03:58:38 2012 | http://epydoc.sourceforge.net |