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 !497 #------------------------------------------------------------406 407 init_ooo() 408 409 #self.ooo_start_cmd = 'oowriter -invisible -accept="socket,host=localhost,port=2002;urp;"' 410 #self.remote_context_uri = "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" 411 412 pipe_name = "uno-gm2lo-%s" % str(random.random())[2:] 413 _log.debug('pipe name: %s', pipe_name) 414 415 #self.ooo_start_cmd = '%s -invisible -norestore -accept="pipe,name=%s;urp"' % ( 416 self.ooo_start_cmd = '%s --norestore --accept="pipe,name=%s;urp" &' % ( 417 writer_binary, 418 pipe_name 419 ) 420 _log.debug('startup command: %s', self.ooo_start_cmd) 421 422 self.remote_context_uri = "uno:pipe,name=%s;urp;StarOffice.ComponentContext" % pipe_name 423 _log.debug('remote context URI: %s', self.remote_context_uri) 424 425 self.resolver_uri = "com.sun.star.bridge.UnoUrlResolver" 426 self.desktop_uri = "com.sun.star.frame.Desktop" 427 428 self.local_context = uno.getComponentContext() 429 self.uri_resolver = self.local_context.ServiceManager.createInstanceWithContext(self.resolver_uri, self.local_context) 430 431 self.__desktop = None432 #--------------------------------------------------------434 if self.__desktop is None: 435 _log.debug('no desktop, no cleanup') 436 return 437 438 try: 439 self.__desktop.terminate() 440 except: 441 _log.exception('cannot terminate OOo desktop')442 #--------------------------------------------------------444 """<filename> must be absolute""" 445 446 if self.desktop is None: 447 _log.error('cannot access OOo desktop') 448 return None 449 450 filename = os.path.expanduser(filename) 451 filename = os.path.abspath(filename) 452 document_uri = uno.systemPathToFileUrl(filename) 453 454 _log.debug('%s -> %s', filename, document_uri) 455 456 doc = self.desktop.loadComponentFromURL(document_uri, "_blank", 0, ()) 457 return doc458 #-------------------------------------------------------- 459 # internal helpers 460 #--------------------------------------------------------462 # later factor this out ! 463 dbcfg = gmCfg.cCfgSQL() 464 self.ooo_startup_settle_time = dbcfg.get2 ( 465 option = u'external.ooo.startup_settle_time', 466 workplace = gmSurgery.gmCurrentPractice().active_workplace, 467 bias = u'workplace', 468 default = 3.0 469 )470 #-------------------------------------------------------- 471 # properties 472 #--------------------------------------------------------474 if self.__desktop is not None: 475 return self.__desktop 476 477 try: 478 self.remote_context = self.uri_resolver.resolve(self.remote_context_uri) 479 except oooNoConnectException: 480 _log.exception('cannot connect to OOo server') 481 _log.info('trying to start OOo server') 482 os.system(self.ooo_start_cmd) 483 self.__get_startup_settle_time() 484 _log.debug('waiting %s seconds for OOo to start up', self.ooo_startup_settle_time) 485 time.sleep(self.ooo_startup_settle_time) # OOo sometimes needs a bit 486 try: 487 self.remote_context = self.uri_resolver.resolve(self.remote_context_uri) 488 except oooNoConnectException: 489 _log.exception('cannot start (or connect to started) OOo server') 490 return None 491 492 self.__desktop = self.remote_context.ServiceManager.createInstanceWithContext(self.desktop_uri, self.remote_context) 493 _log.debug('connection seems established') 494 return self.__desktop495 496 desktop = property(_get_desktop, lambda x:x)499604 #-------------------------------------------------------- 605 # internal helpers 606 #-------------------------------------------------------- 607 608 #============================================================501 502 self.template_file = template_file 503 self.instance_type = instance_type 504 self.ooo_doc = None505 #-------------------------------------------------------- 506 # external API 507 #--------------------------------------------------------509 # connect to OOo 510 ooo_srv = gmOOoConnector() 511 512 # open doc in OOo 513 self.ooo_doc = ooo_srv.open_document(filename = self.template_file) 514 if self.ooo_doc is None: 515 _log.error('cannot open document in OOo') 516 return False 517 518 # listen for close events 519 pat = gmPerson.gmCurrentPatient() 520 pat.locked = True 521 listener = cOOoDocumentCloseListener(document = self) 522 self.ooo_doc.addCloseListener(listener) 523 524 return True525 #-------------------------------------------------------- 528 #--------------------------------------------------------530 531 # new style embedded, implicit placeholders 532 searcher = self.ooo_doc.createSearchDescriptor() 533 searcher.SearchCaseSensitive = False 534 searcher.SearchRegularExpression = True 535 searcher.SearchWords = True 536 searcher.SearchString = handler.placeholder_regex 537 538 placeholder_instance = self.ooo_doc.findFirst(searcher) 539 while placeholder_instance is not None: 540 try: 541 val = handler[placeholder_instance.String] 542 except: 543 val = _('error with placeholder [%s]') % placeholder_instance.String 544 _log.exception(val) 545 546 if val is None: 547 val = _('error with placeholder [%s]') % placeholder_instance.String 548 549 placeholder_instance.String = val 550 placeholder_instance = self.ooo_doc.findNext(placeholder_instance.End, searcher) 551 552 if not old_style_too: 553 return 554 555 # old style "explicit" placeholders 556 text_fields = self.ooo_doc.getTextFields().createEnumeration() 557 while text_fields.hasMoreElements(): 558 text_field = text_fields.nextElement() 559 560 # placeholder ? 561 if not text_field.supportsService('com.sun.star.text.TextField.JumpEdit'): 562 continue 563 # placeholder of type text ? 564 if text_field.PlaceHolderType != 0: 565 continue 566 567 replacement = handler[text_field.PlaceHolder] 568 if replacement is None: 569 continue 570 571 text_field.Anchor.setString(replacement)572 #--------------------------------------------------------574 if filename is not None: 575 target_url = uno.systemPathToFileUrl(os.path.abspath(os.path.expanduser(filename))) 576 save_args = ( 577 oooPropertyValue('Overwrite', 0, True, 0), 578 oooPropertyValue('FormatFilter', 0, 'swriter: StarOffice XML (Writer)', 0) 579 580 ) 581 # "store AS url" stores the doc, marks it unmodified and updates 582 # the internal media descriptor - as opposed to "store TO url" 583 self.ooo_doc.storeAsURL(target_url, save_args) 584 else: 585 self.ooo_doc.store()586 #--------------------------------------------------------588 self.ooo_doc.dispose() 589 pat = gmPerson.gmCurrentPatient() 590 pat.locked = False 591 self.ooo_doc = None592 #--------------------------------------------------------594 # get current file name from OOo, user may have used Save As 595 filename = uno.fileUrlToSystemPath(self.ooo_doc.URL) 596 # tell UI to import the file 597 gmDispatcher.send ( 598 signal = u'import_document_from_file', 599 filename = filename, 600 document_type = self.instance_type, 601 unlock_patient = True 602 ) 603 self.ooo_doc = None610 """Ancestor for forms.""" 611 615 #--------------------------------------------------------694 695 #================================================================ 696 # OOo template forms 697 #----------------------------------------------------------------617 """Parse the template into an instance and replace placeholders with values.""" 618 raise NotImplementedError619 #-------------------------------------------------------- 623 #--------------------------------------------------------625 """Generate output suitable for further processing outside this class, e.g. printing.""" 626 raise NotImplementedError627 #-------------------------------------------------------- 632 #--------------------------------------------------------634 """ 635 A sop to TeX which can't act as a true filter: to delete temporary files 636 """ 637 pass638 #--------------------------------------------------------640 """ 641 Executes the provided command. 642 If command cotains %F. it is substituted with the filename 643 Otherwise, the file is fed in on stdin 644 """ 645 pass646 #--------------------------------------------------------648 """Stores the parameters in the backend. 649 650 - link_obj can be a cursor, a connection or a service name 651 - assigning a cursor to link_obj allows the calling code to 652 group the call to store() into an enclosing transaction 653 (for an example see gmReferral.send_referral()...) 654 """ 655 # some forms may not have values ... 656 if params is None: 657 params = {} 658 patient_clinical = self.patient.get_emr() 659 encounter = patient_clinical.active_encounter['pk_encounter'] 660 # FIXME: get_active_episode is no more 661 #episode = patient_clinical.get_active_episode()['pk_episode'] 662 # generate "forever unique" name 663 cmd = "select name_short || ': <' || name_long || '::' || external_version || '>' from paperwork_templates where pk=%s"; 664 rows = gmPG.run_ro_query('reference', cmd, None, self.pk_def) 665 form_name = None 666 if rows is None: 667 _log.error('error retrieving form def for [%s]' % self.pk_def) 668 elif len(rows) == 0: 669 _log.error('no form def for [%s]' % self.pk_def) 670 else: 671 form_name = rows[0][0] 672 # we didn't get a name but want to store the form anyhow 673 if form_name is None: 674 form_name=time.time() # hopefully unique enough 675 # in one transaction 676 queries = [] 677 # - store form instance in form_instance 678 cmd = "insert into form_instances(fk_form_def, form_name, fk_episode, fk_encounter) values (%s, %s, %s, %s)" 679 queries.append((cmd, [self.pk_def, form_name, episode, encounter])) 680 # - store params in form_data 681 for key in params.keys(): 682 cmd = """ 683 insert into form_data(fk_instance, place_holder, value) 684 values ((select currval('form_instances_pk_seq')), %s, %s::text) 685 """ 686 queries.append((cmd, [key, params[key]])) 687 # - get inserted PK 688 queries.append(("select currval ('form_instances_pk_seq')", [])) 689 status, err = gmPG.run_commit('historica', queries, True) 690 if status is None: 691 _log.error('failed to store form [%s] (%s): %s' % (self.pk_def, form_name, err)) 692 return None 693 return status699 """A forms engine wrapping OOo.""" 700708 709 #================================================================ 710 # AbiWord template forms 711 #----------------------------------------------------------------702 super(self.__class__, self).__init__(template_file = template_file) 703 704 path, ext = os.path.splitext(self.template_filename) 705 if ext in [r'', r'.']: 706 ext = r'.odt' 707 self.instance_filename = r'%s-instance%s' % (path, ext)713 """A forms engine wrapping AbiWord.""" 714 715 placeholder_regex = r'\$<.+?>\$' 716787 #---------------------------------------------------------------- 788 form_engines[u'A'] = cAbiWordForm 789 790 #================================================================ 791 # LaTeX template forms 792 #----------------------------------------------------------------718 719 super(cAbiWordForm, self).__init__(template_file = template_file) 720 721 # detect abiword 722 found, self.abiword_binary = gmShellAPI.detect_external_binary(binary = r'abiword') 723 if not found: 724 raise ImportError('<abiword(.exe)> not found')725 #--------------------------------------------------------727 # should *actually* properly parse the XML 728 729 path, ext = os.path.splitext(self.template_filename) 730 if ext in [r'', r'.']: 731 ext = r'.abw' 732 self.instance_filename = r'%s-instance%s' % (path, ext) 733 734 template_file = codecs.open(self.template_filename, 'rU', 'utf8') 735 instance_file = codecs.open(self.instance_filename, 'wb', 'utf8') 736 737 if self.template is not None: 738 # inject placeholder values 739 data_source.set_placeholder(u'form_name_long', self.template['name_long']) 740 data_source.set_placeholder(u'form_name_short', self.template['name_short']) 741 data_source.set_placeholder(u'form_version', self.template['external_version']) 742 743 for line in template_file: 744 745 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 746 instance_file.write(line) 747 continue 748 749 # 1) find placeholders in this line 750 placeholders_in_line = regex.findall(cAbiWordForm.placeholder_regex, line, regex.IGNORECASE) 751 # 2) and replace them 752 for placeholder in placeholders_in_line: 753 try: 754 val = data_source[placeholder.replace(u'<', u'<').replace(u'>', u'>')] 755 except: 756 val = _('error with placeholder [%s]') % gmTools.xml_escape_string(placeholder) 757 _log.exception(val) 758 759 if val is None: 760 val = _('error with placeholder [%s]') % gmTools.xml_escape_string(placeholder) 761 762 line = line.replace(placeholder, val) 763 764 instance_file.write(line) 765 766 instance_file.close() 767 template_file.close() 768 769 if self.template is not None: 770 # remove temporary placeholders 771 data_source.unset_placeholder(u'form_name_long') 772 data_source.unset_placeholder(u'form_name_short') 773 data_source.unset_placeholder(u'form_version') 774 775 return776 #--------------------------------------------------------778 enc = sys.getfilesystemencoding() 779 cmd = (r'%s %s' % (self.abiword_binary, self.instance_filename.encode(enc))).encode(enc) 780 result = gmShellAPI.run_command_in_shell(command = cmd, blocking = True) 781 self.re_editable_filenames = [] 782 return result783 #--------------------------------------------------------794 """A forms engine wrapping LaTeX.""" 795949 #------------------------------------------------------------ 950 form_engines[u'L'] = cLaTeXForm 951 #============================================================ 952 # Gnuplot template forms 953 #------------------------------------------------------------797 super(self.__class__, self).__init__(template_file = template_file) 798 path, ext = os.path.splitext(self.template_filename) 799 if ext in [r'', r'.']: 800 ext = r'.tex' 801 self.instance_filename = r'%s-instance%s' % (path, ext)802 #--------------------------------------------------------804 805 template_file = codecs.open(self.template_filename, 'rU', 'utf8') 806 instance_file = codecs.open(self.instance_filename, 'wb', 'utf8') 807 808 if self.template is not None: 809 # inject placeholder values 810 data_source.set_placeholder(u'form_name_long', self.template['name_long']) 811 data_source.set_placeholder(u'form_name_short', self.template['name_short']) 812 data_source.set_placeholder(u'form_version', self.template['external_version']) 813 814 for line in template_file: 815 816 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 817 instance_file.write(line) 818 continue 819 820 # 1) find placeholders in this line 821 placeholders_in_line = regex.findall(data_source.placeholder_regex, line, regex.IGNORECASE) 822 # 2) and replace them 823 for placeholder in placeholders_in_line: 824 try: 825 val = data_source[placeholder] 826 except: 827 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder) 828 _log.exception(val) 829 830 if val is None: 831 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder) 832 833 line = line.replace(placeholder, val) 834 835 instance_file.write(line) 836 837 instance_file.close() 838 self.re_editable_filenames = [self.instance_filename] 839 template_file.close() 840 841 if self.template is not None: 842 # remove temporary placeholders 843 data_source.unset_placeholder(u'form_name_long') 844 data_source.unset_placeholder(u'form_name_short') 845 data_source.unset_placeholder(u'form_version') 846 847 return848 #--------------------------------------------------------850 851 mimetypes = [ 852 u'application/x-latex', 853 u'application/x-tex', 854 u'text/plain' 855 ] 856 857 for mimetype in mimetypes: 858 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename) 859 if editor_cmd is not None: 860 break 861 862 if editor_cmd is None: 863 # LaTeX code is text: also consider text *viewers* 864 # since pretty much any of them will be an editor as well 865 for mimetype in mimetypes: 866 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename) 867 if editor_cmd is not None: 868 break 869 870 # last resort 871 if editor_cmd is None: 872 if os.name == 'nt': 873 editor_cmd = u'notepad.exe %s' % self.instance_filename 874 else: 875 editor_cmd = u'sensible-editor %s' % self.instance_filename 876 877 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 878 self.re_editable_filenames = [self.instance_filename] 879 880 return result881 #--------------------------------------------------------883 884 if instance_file is None: 885 instance_file = self.instance_filename 886 887 try: 888 open(instance_file, 'r').close() 889 except: 890 _log.exception('cannot access form instance file [%s]', instance_file) 891 gmLog2.log_stack_trace() 892 return None 893 894 self.instance_filename = instance_file 895 896 _log.debug('ignoring <format> directive [%s], generating PDF', format) 897 898 # create sandbox for LaTeX to play in 899 sandbox_dir = os.path.splitext(self.template_filename)[0] 900 _log.debug('LaTeX sandbox directory: [%s]', sandbox_dir) 901 902 old_cwd = os.getcwd() 903 _log.debug('CWD: [%s]', old_cwd) 904 905 gmTools.mkdir(sandbox_dir) 906 907 os.chdir(sandbox_dir) 908 try: 909 sandboxed_instance_filename = os.path.join(sandbox_dir, os.path.split(self.instance_filename)[1]) 910 shutil.move(self.instance_filename, sandboxed_instance_filename) 911 912 # LaTeX can need up to three runs to get cross references et al right 913 if platform.system() == 'Windows': 914 draft_cmd = r'pdflatex.exe -draftmode -interaction nonstopmode %s' % sandboxed_instance_filename 915 final_cmd = r'pdflatex.exe -interaction nonstopmode %s' % sandboxed_instance_filename 916 else: 917 draft_cmd = r'pdflatex -draftmode -interaction nonstopmode %s' % sandboxed_instance_filename 918 final_cmd = r'pdflatex -interaction nonstopmode %s' % sandboxed_instance_filename 919 for run_cmd in [draft_cmd, draft_cmd, final_cmd]: 920 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]): 921 _log.error('problem running pdflatex, cannot generate form output') 922 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdflatex. Cannot turn LaTeX template into PDF.'), beep = True) 923 os.chdir(old_cwd) 924 return None 925 finally: 926 os.chdir(old_cwd) 927 928 sandboxed_pdf_name = u'%s.pdf' % os.path.splitext(sandboxed_instance_filename)[0] 929 target_dir = os.path.split(self.instance_filename)[0] 930 try: 931 shutil.move(sandboxed_pdf_name, target_dir) 932 except IOError: 933 _log.exception('cannot move sandboxed PDF: %s -> %s', sandboxed_pdf_name, target_dir) 934 gmDispatcher.send(signal = 'statustext', msg = _('Sandboxed PDF output file cannot be moved.'), beep = True) 935 return None 936 937 final_pdf_name = u'%s.pdf' % os.path.splitext(self.instance_filename)[0] 938 939 try: 940 open(final_pdf_name, 'r').close() 941 except IOError: 942 _log.exception('cannot open target PDF: %s', final_pdf_name) 943 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True) 944 return None 945 946 self.final_output_filenames = [final_pdf_name] 947 948 return final_pdf_name955 """A forms engine wrapping Gnuplot.""" 956 957 #-------------------------------------------------------- 961 #--------------------------------------------------------1006 #------------------------------------------------------------ 1007 form_engines[u'G'] = cGnuplotForm 1008 1009 #============================================================ 1010 # fPDF form engine 1011 #------------------------------------------------------------963 """Allow editing the instance of the template.""" 964 self.re_editable_filenames = [] 965 return True966 #--------------------------------------------------------968 """Generate output suitable for further processing outside this class, e.g. printing. 969 970 Expects .data_filename to be set. 971 """ 972 self.conf_filename = gmTools.get_unique_filename(prefix = 'gm2gpl-', suffix = '.conf') 973 fname_file = codecs.open(self.conf_filename, 'wb', 'utf8') 974 fname_file.write('# setting the gnuplot data file\n') 975 fname_file.write("gm2gpl_datafile = '%s'\n" % self.data_filename) 976 fname_file.close() 977 978 # FIXME: cater for configurable path 979 if platform.system() == 'Windows': 980 exec_name = 'gnuplot.exe' 981 else: 982 exec_name = 'gnuplot' 983 984 args = [exec_name, '-p', self.conf_filename, self.template_filename] 985 _log.debug('plotting args: %s' % str(args)) 986 987 try: 988 gp = subprocess.Popen ( 989 args = args, 990 close_fds = True 991 ) 992 except (OSError, ValueError, subprocess.CalledProcessError): 993 _log.exception('there was a problem executing gnuplot') 994 gmDispatcher.send(signal = u'statustext', msg = _('Error running gnuplot. Cannot plot data.'), beep = True) 995 return 996 997 gp.communicate() 998 999 self.final_output_filenames = [ 1000 self.conf_filename, 1001 self.data_filename, 1002 self.template_filename 1003 ] 1004 1005 return1013 """A forms engine wrapping PDF forms. 1014 1015 Johann Felix Soden <johfel@gmx.de> helped with this. 1016 1017 http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf 1018 1019 http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/fdf_data_exchange.pdf 1020 """ 10211218 #------------------------------------------------------------ 1219 form_engines[u'P'] = cPDFForm 1220 1221 #============================================================ 1222 # older code 1223 #------------------------------------------------------------1023 1024 super(cPDFForm, self).__init__(template_file = template_file) 1025 1026 # detect pdftk 1027 found, self.pdftk_binary = gmShellAPI.detect_external_binary(binary = r'pdftk') 1028 if not found: 1029 raise ImportError('<pdftk(.exe)> not found') 1030 return # should be superfluous, actually 1031 1032 enc = sys.getfilesystemencoding() 1033 self.pdftk_binary = self.pdftk_binary.encode(enc) 1034 1035 base_name, ext = os.path.splitext(self.template_filename) 1036 self.fdf_dumped_filename = (u'%s.fdf' % base_name).encode(enc) 1037 self.fdf_replaced_filename = (u'%s-replaced.fdf' % base_name).encode(enc) 1038 self.pdf_filled_filename = (u'%s-filled.pdf' % base_name).encode(enc) 1039 self.pdf_flattened_filename = (u'%s-filled-flattened.pdf' % base_name).encode(enc)1040 #--------------------------------------------------------1042 1043 # dump form fields from template 1044 cmd_line = [ 1045 self.pdftk_binary, 1046 self.template_filename, 1047 r'generate_fdf', 1048 r'output', 1049 self.fdf_dumped_filename 1050 ] 1051 _log.debug(u' '.join(cmd_line)) 1052 try: 1053 pdftk = subprocess.Popen(cmd_line) 1054 except OSError: 1055 _log.exception('cannot run <pdftk> (dump data from form)') 1056 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot extract fields from PDF form template.'), beep = True) 1057 return False 1058 1059 pdftk.communicate() 1060 if pdftk.returncode != 0: 1061 _log.error('<pdftk> returned [%s], failed to dump data from PDF form into FDF', pdftk.returncode) 1062 return False 1063 1064 # parse dumped FDF file for "/V (...)" records 1065 # and replace placeholders therein 1066 fdf_dumped_file = open(self.fdf_dumped_filename, 'rbU') 1067 fdf_replaced_file = codecs.open(self.fdf_replaced_filename, 'wb') 1068 1069 string_value_regex = r'\s*/V\s*\(.+\)\s*$' 1070 for line in fdf_dumped_file: 1071 if not regex.match(string_value_regex, line): 1072 fdf_replaced_file.write(line) 1073 continue 1074 1075 # strip cruft around the string value 1076 raw_str_val = line.strip() # remove framing whitespace 1077 raw_str_val = raw_str_val[2:] # remove leading "/V" 1078 raw_str_val = raw_str_val.lstrip() # remove whitespace between "/V" and "(" 1079 raw_str_val = raw_str_val[1:] # remove opening "(" 1080 raw_str_val = raw_str_val[2:] # remove BOM-16-BE 1081 raw_str_val = raw_str_val.rstrip() # remove trailing whitespace 1082 raw_str_val = raw_str_val[:-1] # remove closing ")" 1083 1084 # work on FDF escapes 1085 raw_str_val = raw_str_val.replace('\(', '(') # remove escaping of "(" 1086 raw_str_val = raw_str_val.replace('\)', ')') # remove escaping of ")" 1087 1088 # by now raw_str_val should contain the actual 1089 # string value, albeit encoded as UTF-16, so 1090 # decode it into a unicode object, 1091 # split multi-line fields on "\n" literal 1092 raw_str_lines = raw_str_val.split('\x00\\n') 1093 value_template_lines = [] 1094 for raw_str_line in raw_str_lines: 1095 value_template_lines.append(raw_str_line.decode('utf_16_be')) 1096 1097 replaced_lines = [] 1098 for value_template in value_template_lines: 1099 # find any placeholders within 1100 placeholders_in_value = regex.findall(data_source.placeholder_regex, value_template, regex.IGNORECASE) 1101 for placeholder in placeholders_in_value: 1102 try: 1103 replacement = data_source[placeholder] 1104 except: 1105 _log.exception(replacement) 1106 replacement = _('error with placeholder [%s]') % placeholder 1107 if replacement is None: 1108 replacement = _('error with placeholder [%s]') % placeholder 1109 value_template = value_template.replace(placeholder, replacement) 1110 1111 value_template = value_template.encode('utf_16_be') 1112 1113 if len(placeholders_in_value) > 0: 1114 value_template = value_template.replace(r'(', r'\(') 1115 value_template = value_template.replace(r')', r'\)') 1116 1117 replaced_lines.append(value_template) 1118 1119 replaced_line = '\x00\\n'.join(replaced_lines) 1120 1121 fdf_replaced_file.write('/V (') 1122 fdf_replaced_file.write(codecs.BOM_UTF16_BE) 1123 fdf_replaced_file.write(replaced_line) 1124 fdf_replaced_file.write(')\n') 1125 1126 fdf_replaced_file.close() 1127 fdf_dumped_file.close() 1128 1129 # merge replaced data back into form 1130 cmd_line = [ 1131 self.pdftk_binary, 1132 self.template_filename, 1133 r'fill_form', 1134 self.fdf_replaced_filename, 1135 r'output', 1136 self.pdf_filled_filename 1137 ] 1138 _log.debug(u' '.join(cmd_line)) 1139 try: 1140 pdftk = subprocess.Popen(cmd_line) 1141 except OSError: 1142 _log.exception('cannot run <pdftk> (merge data into form)') 1143 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot fill in PDF form template.'), beep = True) 1144 return False 1145 1146 pdftk.communicate() 1147 if pdftk.returncode != 0: 1148 _log.error('<pdftk> returned [%s], failed to merge FDF data into PDF form', pdftk.returncode) 1149 return False 1150 1151 return True1152 #--------------------------------------------------------1154 mimetypes = [ 1155 u'application/pdf', 1156 u'application/x-pdf' 1157 ] 1158 1159 for mimetype in mimetypes: 1160 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.pdf_filled_filename) 1161 if editor_cmd is not None: 1162 break 1163 1164 if editor_cmd is None: 1165 _log.debug('editor cmd not found, trying viewer cmd') 1166 for mimetype in mimetypes: 1167 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.pdf_filled_filename) 1168 if editor_cmd is not None: 1169 break 1170 1171 if editor_cmd is None: 1172 return False 1173 1174 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 1175 1176 path, fname = os.path.split(self.pdf_filled_filename) 1177 candidate = os.path.join(gmTools.gmPaths().home_dir, fname) 1178 1179 if os.access(candidate, os.R_OK): 1180 _log.debug('filled-in PDF found: %s', candidate) 1181 os.rename(self.pdf_filled_filename, self.pdf_filled_filename + '.bak') 1182 shutil.move(candidate, path) 1183 else: 1184 _log.debug('filled-in PDF not found: %s', candidate) 1185 1186 self.re_editable_filenames = [self.pdf_filled_filename] 1187 1188 return result1189 #--------------------------------------------------------1191 """Generate output suitable for further processing outside this class, e.g. printing.""" 1192 1193 # eventually flatten the filled in form so we 1194 # can keep both a flattened and an editable copy: 1195 cmd_line = [ 1196 self.pdftk_binary, 1197 self.pdf_filled_filename, 1198 r'output', 1199 self.pdf_flattened_filename, 1200 r'flatten' 1201 ] 1202 _log.debug(u' '.join(cmd_line)) 1203 try: 1204 pdftk = subprocess.Popen(cmd_line) 1205 except OSError: 1206 _log.exception('cannot run <pdftk> (flatten filled in form)') 1207 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot flatten filled in PDF form.'), beep = True) 1208 return None 1209 1210 pdftk.communicate() 1211 if pdftk.returncode != 0: 1212 _log.error('<pdftk> returned [%s], failed to flatten filled in PDF form', pdftk.returncode) 1213 return None 1214 1215 self.final_output_filenames = [self.pdf_flattened_filename] 1216 1217 return self.pdf_flattened_filename1225 """A forms engine wrapping LaTeX. 1226 """ 12301283 1284 1285 1286 1287 #================================================================ 1288 # define a class for HTML forms (for printing) 1289 #================================================================1232 try: 1233 latex = Cheetah.Template.Template (self.template, filter=LaTeXFilter, searchList=[params]) 1234 # create a 'sandbox' directory for LaTeX to play in 1235 self.tmp = tempfile.mktemp () 1236 os.makedirs (self.tmp) 1237 self.oldcwd = os.getcwd () 1238 os.chdir (self.tmp) 1239 stdin = os.popen ("latex", "w", 2048) 1240 stdin.write (str (latex)) #send text. LaTeX spits it's output into stdout 1241 # FIXME: send LaTeX output to the logger 1242 stdin.close () 1243 if not gmShellAPI.run_command_in_shell("dvips texput.dvi -o texput.ps", blocking=True): 1244 raise FormError ('DVIPS returned error') 1245 except EnvironmentError, e: 1246 _log.error(e.strerror) 1247 raise FormError (e.strerror) 1248 return file ("texput.ps")12491251 """ 1252 For testing purposes, runs Xdvi on the intermediate TeX output 1253 WARNING: don't try this on Windows 1254 """ 1255 gmShellAPI.run_command_in_shell("xdvi texput.dvi", blocking=True)12561258 if "%F" in command: 1259 command.replace ("%F", "texput.ps") 1260 else: 1261 command = "%s < texput.ps" % command 1262 try: 1263 if not gmShellAPI.run_command_in_shell(command, blocking=True): 1264 _log.error("external command %s returned non-zero" % command) 1265 raise FormError ('external command %s returned error' % command) 1266 except EnvironmentError, e: 1267 _log.error(e.strerror) 1268 raise FormError (e.strerror) 1269 return True12701272 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print') 1273 self.exe (command)12741291 """This class can create XML document from requested data, 1292 then process it with XSLT template and display results 1293 """ 1294 1295 # FIXME: make the path configurable ? 1296 _preview_program = u'oowriter ' #this program must be in the system PATH 12971374 1375 1376 #===================================================== 1377 #class LaTeXFilter(Cheetah.Filters.Filter):1299 1300 if template is None: 1301 raise ValueError(u'%s: cannot create form instance without a template' % __name__) 1302 1303 cFormEngine.__init__(self, template = template) 1304 1305 self._FormData = None 1306 1307 # here we know/can assume that the template was stored as a utf-8 1308 # encoded string so use that conversion to create unicode: 1309 #self._XSLTData = unicode(str(template.template_data), 'UTF-8') 1310 # but in fact, unicode() knows how to handle buffers, so simply: 1311 self._XSLTData = unicode(self.template.template_data, 'UTF-8', 'strict') 1312 1313 # we must still devise a method of extracting the SQL query: 1314 # - either by retrieving it from a particular tag in the XSLT or 1315 # - by making the stored template actually be a dict which, unpickled, 1316 # has the keys "xslt" and "sql" 1317 self._SQL_query = u'select 1' #this sql query must output valid xml1318 #-------------------------------------------------------- 1319 # external API 1320 #--------------------------------------------------------1322 """get data from backend and process it with XSLT template to produce readable output""" 1323 1324 # extract SQL (this is wrong but displays what is intended) 1325 xslt = libxml2.parseDoc(self._XSLTData) 1326 root = xslt.children 1327 for child in root: 1328 if child.type == 'element': 1329 self._SQL_query = child.content 1330 break 1331 1332 # retrieve data from backend 1333 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': self._SQL_query, 'args': sql_parameters}], get_col_idx = False) 1334 1335 __header = '<?xml version="1.0" encoding="UTF-8"?>\n' 1336 __body = rows[0][0] 1337 1338 # process XML data according to supplied XSLT, producing HTML 1339 self._XMLData =__header + __body 1340 style = libxslt.parseStylesheetDoc(xslt) 1341 xml = libxml2.parseDoc(self._XMLData) 1342 html = style.applyStylesheet(xml, None) 1343 self._FormData = html.serialize() 1344 1345 style.freeStylesheet() 1346 xml.freeDoc() 1347 html.freeDoc()1348 #--------------------------------------------------------1350 if self._FormData is None: 1351 raise ValueError, u'Preview request for empty form. Make sure the form is properly initialized and process() was performed' 1352 1353 fname = gmTools.get_unique_filename(prefix = u'gm_XSLT_form-', suffix = u'.html') 1354 #html_file = os.open(fname, 'wb') 1355 #html_file.write(self._FormData.encode('UTF-8')) 1356 html_file = codecs.open(fname, 'wb', 'utf8', 'strict') # or 'replace' ? 1357 html_file.write(self._FormData) 1358 html_file.close() 1359 1360 cmd = u'%s %s' % (self.__class__._preview_program, fname) 1361 1362 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = False): 1363 _log.error('%s: cannot launch report preview program' % __name__) 1364 return False 1365 1366 #os.unlink(self.filename) #delete file 1367 #FIXME: under Windows the temp file is deleted before preview program gets it (under Linux it works OK) 1368 1369 return True1370 #--------------------------------------------------------1417 1418 1419 #=========================================================== 1422 1423 #============================================================ 1424 # convenience functions 1425 #------------------------------------------------------------1380 """ 1381 Convience function to escape ISO-Latin-1 strings for TeX output 1382 WARNING: not all ISO-Latin-1 characters are expressible in TeX 1383 FIXME: nevertheless, there are a few more we could support 1384 1385 Also intelligently convert lists and tuples into TeX-style table lines 1386 """ 1387 if type (item) is types.UnicodeType or type (item) is types.StringType: 1388 item = item.replace ("\\", "\\backslash") # I wonder about this, do we want users to be able to use raw TeX? 1389 item = item.replace ("&", "\\&") 1390 item = item.replace ("$", "\\$") 1391 item = item.replace ('"', "") # okay, that's not right, but easiest solution for now 1392 item = item.replace ("\n", "\\\\ ") 1393 if len (item.strip ()) == 0: 1394 item = "\\relax " # sometimes TeX really hates empty strings, this seems to mollify it 1395 # FIXME: cover all of ISO-Latin-1 which can be expressed in TeX 1396 if type (item) is types.UnicodeType: 1397 item = item.encode ('latin-1', 'replace') 1398 trans = {'ß':'\\ss{}', 'ä': '\\"{a}', 'Ä' :'\\"{A}', 'ö': '\\"{o}', 'Ö': '\\"{O}', 'ü': '\\"{u}', 'Ü': '\\"{U}', 1399 '\x8a':'\\v{S}', '\x8a':'\\OE{}', '\x9a':'\\v{s}', '\x9c': '\\oe{}', '\a9f':'\\"{Y}', #Microsloth extensions 1400 '\x86': '{\\dag}', '\x87': '{\\ddag}', '\xa7':'{\\S}', '\xb6': '{\\P}', '\xa9': '{\\copyright}', '\xbf': '?`', 1401 '\xc0':'\\`{A}', '\xa1': "\\'{A}", '\xa2': '\\^{A}', '\xa3':'\\~{A}', '\\xc5': '{\AA}', 1402 '\xc7':'\\c{C}', '\xc8':'\\`{E}', 1403 '\xa1': '!`', 1404 '\xb5':'$\mu$', '\xa3': '\pounds{}', '\xa2':'cent'} 1405 for k, i in trans.items (): 1406 item = item.replace (k, i) 1407 elif type (item) is types.ListType or type (item) is types.TupleType: 1408 item = string.join ([self.filter (i, ' & ') for i in item], table_sep) 1409 elif item is None: 1410 item = '\\relax % Python None\n' 1411 elif type (item) is types.IntType or type (item) is types.FloatType: 1412 item = str (item) 1413 else: 1414 item = str (item) 1415 _log.warning("unknown type %s, string %s" % (type (item), item)) 1416 return item1427 """ 1428 Instantiates a FormEngine based on the form ID or name from the backend 1429 """ 1430 try: 1431 # it's a number: match to form ID 1432 id = int (id) 1433 cmd = 'select template, engine, pk from paperwork_templates where pk = %s' 1434 except ValueError: 1435 # it's a string, match to the form's name 1436 # FIXME: can we somehow OR like this: where name_short=%s OR name_long=%s ? 1437 cmd = 'select template, engine, flags, pk from paperwork_templates where name_short = %s' 1438 result = gmPG.run_ro_query ('reference', cmd, None, id) 1439 if result is None: 1440 _log.error('error getting form [%s]' % id) 1441 raise gmExceptions.FormError ('error getting form [%s]' % id) 1442 if len(result) == 0: 1443 _log.error('no form [%s] found' % id) 1444 raise gmExceptions.FormError ('no such form found [%s]' % id) 1445 if result[0][1] == 'L': 1446 return LaTeXForm (result[0][2], result[0][0]) 1447 elif result[0][1] == 'T': 1448 return TextForm (result[0][2], result[0][0]) 1449 else: 1450 _log.error('no form engine [%s] for form [%s]' % (result[0][1], id)) 1451 raise FormError ('no engine [%s] for form [%s]' % (result[0][1], id))1452 #------------------------------------------------------------- 1459 #------------------------------------------------------------- 1460 1461 test_letter = """ 1462 \\documentclass{letter} 1463 \\address{ $DOCTOR \\\\ 1464 $DOCTORADDRESS} 1465 \\signature{$DOCTOR} 1466 1467 \\begin{document} 1468 \\begin{letter}{$RECIPIENTNAME \\\\ 1469 $RECIPIENTADDRESS} 1470 1471 \\opening{Dear $RECIPIENTNAME} 1472 1473 \\textbf{Re:} $PATIENTNAME, DOB: $DOB, $PATIENTADDRESS \\\\ 1474 1475 $TEXT 1476 1477 \\ifnum$INCLUDEMEDS>0 1478 \\textbf{Medications List} 1479 1480 \\begin{tabular}{lll} 1481 $MEDSLIST 1482 \\end{tabular} 1483 \\fi 1484 1485 \\ifnum$INCLUDEDISEASES>0 1486 \\textbf{Disease List} 1487 1488 \\begin{tabular}{l} 1489 $DISEASELIST 1490 \\end{tabular} 1491 \\fi 1492 1493 \\closing{$CLOSING} 1494 1495 \\end{letter} 1496 \\end{document} 1497 """ 1498 14991501 f = open('../../test-area/ian/terry-form.tex') 1502 params = { 1503 'RECIPIENT': "Dr. R. Terry\n1 Main St\nNewcastle", 1504 'DOCTORSNAME': 'Ian Haywood', 1505 'DOCTORSADDRESS': '1 Smith St\nMelbourne', 1506 'PATIENTNAME':'Joe Bloggs', 1507 'PATIENTADDRESS':'18 Fred St\nMelbourne', 1508 'REQUEST':'echocardiogram', 1509 'THERAPY':'on warfarin', 1510 'CLINICALNOTES':"""heard new murmur 1511 Here's some 1512 crap to demonstrate how it can cover multiple lines.""", 1513 'COPYADDRESS':'Karsten Hilbert\nLeipzig, Germany', 1514 'ROUTINE':1, 1515 'URGENT':0, 1516 'FAX':1, 1517 'PHONE':1, 1518 'PENSIONER':1, 1519 'VETERAN':0, 1520 'PADS':0, 1521 'INSTRUCTIONS':u'Take the blue pill, Neo' 1522 } 1523 form = LaTeXForm (1, f.read()) 1524 form.process (params) 1525 form.xdvi () 1526 form.cleanup ()15271529 form = LaTeXForm (2, test_letter) 1530 params = {'RECIPIENTNAME':'Dr. Richard Terry', 1531 'RECIPIENTADDRESS':'1 Main St\nNewcastle', 1532 'DOCTOR':'Dr. Ian Haywood', 1533 'DOCTORADDRESS':'1 Smith St\nMelbourne', 1534 'PATIENTNAME':'Joe Bloggs', 1535 'PATIENTADDRESS':'18 Fred St, Melbourne', 1536 'TEXT':"""This is the main text of the referral letter""", 1537 'DOB':'12/3/65', 1538 'INCLUDEMEDS':1, 1539 'MEDSLIST':[["Amoxycillin", "500mg", "TDS"], ["Perindopril", "4mg", "OD"]], 1540 'INCLUDEDISEASES':0, 'DISEASELIST':'', 1541 'CLOSING':'Yours sincerely,' 1542 } 1543 form.process (params) 1544 print os.getcwd () 1545 form.xdvi () 1546 form.cleanup ()1547 #------------------------------------------------------------1549 template = open('../../test-area/ian/Formularkopf-DE.tex') 1550 form = LaTeXForm(template=template.read()) 1551 params = { 1552 'PATIENT LASTNAME': 'Kirk', 1553 'PATIENT FIRSTNAME': 'James T.', 1554 'PATIENT STREET': 'Hauptstrasse', 1555 'PATIENT ZIP': '02999', 1556 'PATIENT TOWN': 'Gross Saerchen', 1557 'PATIENT DOB': '22.03.1931' 1558 } 1559 form.process(params) 1560 form.xdvi() 1561 form.cleanup()1562 1563 #============================================================ 1564 # main 1565 #------------------------------------------------------------ 1566 if __name__ == '__main__': 1567 1568 if len(sys.argv) < 2: 1569 sys.exit() 1570 1571 if sys.argv[1] != 'test': 1572 sys.exit() 1573 1574 from Gnumed.pycommon import gmDateTime 1575 gmDateTime.init() 1576 1577 #-------------------------------------------------------- 1578 # OOo 1579 #--------------------------------------------------------1581 init_ooo()1582 #-------------------------------------------------------- 1587 #--------------------------------------------------------1589 srv = gmOOoConnector() 1590 doc = srv.open_document(filename = sys.argv[2]) 1591 print "document:", doc1592 #--------------------------------------------------------1594 doc = cOOoLetter(template_file = sys.argv[2]) 1595 doc.open_in_ooo() 1596 print "document:", doc 1597 raw_input('press <ENTER> to continue') 1598 doc.show() 1599 #doc.replace_placeholders() 1600 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1601 # doc = None 1602 # doc.close_in_ooo() 1603 raw_input('press <ENTER> to continue')1604 #--------------------------------------------------------1606 try: 1607 doc = open_uri_in_ooo(filename=sys.argv[1]) 1608 except: 1609 _log.exception('cannot open [%s] in OOo' % sys.argv[1]) 1610 raise 1611 1612 class myCloseListener(unohelper.Base, oooXCloseListener): 1613 def disposing(self, evt): 1614 print "disposing:"1615 def notifyClosing(self, evt): 1616 print "notifyClosing:" 1617 def queryClosing(self, evt, owner): 1618 # owner is True/False whether I am the owner of the doc 1619 print "queryClosing:" 1620 1621 l = myCloseListener() 1622 doc.addCloseListener(l) 1623 1624 tfs = doc.getTextFields().createEnumeration() 1625 print tfs 1626 print dir(tfs) 1627 while tfs.hasMoreElements(): 1628 tf = tfs.nextElement() 1629 if tf.supportsService('com.sun.star.text.TextField.JumpEdit'): 1630 print tf.getPropertyValue('PlaceHolder') 1631 print " ", tf.getPropertyValue('Hint') 1632 1633 # doc.close(True) # closes but leaves open the dedicated OOo window 1634 doc.dispose() # closes and disposes of the OOo window 1635 #--------------------------------------------------------1637 pat = gmPersonSearch.ask_for_patient() 1638 if pat is None: 1639 return 1640 gmPerson.set_active_patient(patient = pat) 1641 1642 doc = cOOoLetter(template_file = sys.argv[2]) 1643 doc.open_in_ooo() 1644 print doc 1645 doc.show() 1646 #doc.replace_placeholders() 1647 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1648 doc = None 1649 # doc.close_in_ooo() 1650 raw_input('press <ENTER> to continue')1651 #-------------------------------------------------------- 1652 # other 1653 #--------------------------------------------------------1655 template = cFormTemplate(aPK_obj = sys.argv[2]) 1656 print template 1657 print template.export_to_file()1658 #--------------------------------------------------------1660 template = cFormTemplate(aPK_obj = sys.argv[2]) 1661 template.update_template_from_file(filename = sys.argv[3])1662 #--------------------------------------------------------1664 pat = gmPersonSearch.ask_for_patient() 1665 if pat is None: 1666 return 1667 gmPerson.set_active_patient(patient = pat) 1668 1669 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1670 1671 path = os.path.abspath(sys.argv[2]) 1672 form = cLaTeXForm(template_file = path) 1673 1674 from Gnumed.wxpython import gmMacro 1675 ph = gmMacro.gmPlaceholderHandler() 1676 ph.debug = True 1677 instance_file = form.substitute_placeholders(data_source = ph) 1678 pdf_name = form.generate_output(instance_file = instance_file) 1679 print "final PDF file is:", pdf_name1680 #--------------------------------------------------------1682 pat = gmPersonSearch.ask_for_patient() 1683 if pat is None: 1684 return 1685 gmPerson.set_active_patient(patient = pat) 1686 1687 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1688 1689 path = os.path.abspath(sys.argv[2]) 1690 form = cPDFForm(template_file = path) 1691 1692 from Gnumed.wxpython import gmMacro 1693 ph = gmMacro.gmPlaceholderHandler() 1694 ph.debug = True 1695 instance_file = form.substitute_placeholders(data_source = ph) 1696 pdf_name = form.generate_output(instance_file = instance_file) 1697 print "final PDF file is:", pdf_name1698 #--------------------------------------------------------1700 pat = gmPersonSearch.ask_for_patient() 1701 if pat is None: 1702 return 1703 gmPerson.set_active_patient(patient = pat) 1704 1705 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1706 1707 path = os.path.abspath(sys.argv[2]) 1708 form = cAbiWordForm(template_file = path) 1709 1710 from Gnumed.wxpython import gmMacro 1711 ph = gmMacro.gmPlaceholderHandler() 1712 ph.debug = True 1713 instance_file = form.substitute_placeholders(data_source = ph) 1714 form.edit() 1715 final_name = form.generate_output(instance_file = instance_file) 1716 print "final file is:", final_name1717 #-------------------------------------------------------- 1718 #-------------------------------------------------------- 1719 # now run the tests 1720 #test_au() 1721 #test_de() 1722 1723 # OOo 1724 #test_init_ooo() 1725 #test_ooo_connect() 1726 #test_open_ooo_doc_from_srv() 1727 #test_open_ooo_doc_from_letter() 1728 #play_with_ooo() 1729 #test_cOOoLetter() 1730 1731 #test_cFormTemplate() 1732 #set_template_from_file() 1733 #test_latex_form() 1734 #test_pdf_form() 1735 test_abiword_form() 1736 1737 #============================================================ 1738
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Wed Jun 13 03:58:51 2012 | http://epydoc.sourceforge.net |