Package Gnumed :: Package business :: Module gmForms
[frames] | no frames]

Source Code for Module Gnumed.business.gmForms

   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 
   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, codecs, re as regex 
  14  import shutil, random, platform, subprocess 
  15  import socket                                                                           # needed for OOo on Windows 
  16  #, libxml2, libxslt 
  17   
  18   
  19  if __name__ == '__main__': 
  20          sys.path.insert(0, '../../') 
  21  from Gnumed.pycommon import gmTools, gmBorg, gmMatchProvider, gmExceptions, gmDispatcher 
  22  from Gnumed.pycommon import gmPG2, gmBusinessDBObject, gmCfg, gmShellAPI, gmMimeLib, gmLog2 
  23  from Gnumed.business import gmPerson, gmSurgery 
  24   
  25   
  26  _log = logging.getLogger('gm.forms') 
  27  _log.info(__version__) 
  28   
  29  #============================================================ 
  30  # this order is also used in choice boxes for the engine 
  31  form_engine_abbrevs = [u'O', u'L', u'I', u'G'] 
  32   
  33  form_engine_names = { 
  34          u'O': 'OpenOffice', 
  35          u'L': 'LaTeX', 
  36          u'I': 'Image editor', 
  37          u'G': 'Gnuplot script' 
  38  } 
  39   
  40  form_engine_template_wildcards = { 
  41          u'O': u'*.o?t', 
  42          u'L': u'*.tex', 
  43          u'G': u'*.gpl' 
  44  } 
  45   
  46  # is filled in further below after each engine is defined 
  47  form_engines = {} 
  48   
  49  #============================================================ 
  50  # match providers 
  51  #============================================================ 
52 -class cFormTemplateNameLong_MatchProvider(gmMatchProvider.cMatchProvider_SQL2):
53
54 - def __init__(self):
55 56 query = u""" 57 select name_long, name_long 58 from ref.v_paperwork_templates 59 where name_long %(fragment_condition)s 60 order by name_long 61 """ 62 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
63 #============================================================
64 -class cFormTemplateNameShort_MatchProvider(gmMatchProvider.cMatchProvider_SQL2):
65
66 - def __init__(self):
67 68 query = u""" 69 select name_short, name_short 70 from ref.v_paperwork_templates 71 where name_short %(fragment_condition)s 72 order by name_short 73 """ 74 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
75 #============================================================
76 -class cFormTemplateType_MatchProvider(gmMatchProvider.cMatchProvider_SQL2):
77
78 - def __init__(self):
79 80 query = u""" 81 select * from ( 82 select pk, _(name) as l10n_name from ref.form_types 83 where _(name) %(fragment_condition)s 84 85 union 86 87 select pk, _(name) as l10n_name from ref.form_types 88 where name %(fragment_condition)s 89 ) as union_result 90 order by l10n_name 91 """ 92 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
93 #============================================================
94 -class cFormTemplate(gmBusinessDBObject.cBusinessDBObject):
95 96 _cmd_fetch_payload = u'select * from ref.v_paperwork_templates where pk_paperwork_template = %s' 97 98 _cmds_store_payload = [ 99 u"""update ref.paperwork_templates set 100 name_short = %(name_short)s, 101 name_long = %(name_long)s, 102 fk_template_type = %(pk_template_type)s, 103 instance_type = %(instance_type)s, 104 engine = %(engine)s, 105 in_use = %(in_use)s, 106 filename = %(filename)s, 107 external_version = %(external_version)s 108 where 109 pk = %(pk_paperwork_template)s and 110 xmin = %(xmin_paperwork_template)s 111 """, 112 u"""select xmin_paperwork_template from ref.v_paperwork_templates where pk_paperwork_template = %(pk_paperwork_template)s""" 113 ] 114 115 _updatable_fields = [ 116 u'name_short', 117 u'name_long', 118 u'external_version', 119 u'pk_template_type', 120 u'instance_type', 121 u'engine', 122 u'in_use', 123 u'filename' 124 ] 125 126 _suffix4engine = { 127 u'O': u'.ott', 128 u'L': u'.tex', 129 u'T': u'.txt', 130 u'X': u'.xslt', 131 u'I': u'.img' 132 } 133 134 #--------------------------------------------------------
135 - def _get_template_data(self):
136 """The template itself better not be arbitrarily large unless you can handle that. 137 138 Note that the data type returned will be a buffer.""" 139 140 cmd = u'SELECT data FROM ref.paperwork_templates WHERE pk = %(pk)s' 141 rows, idx = gmPG2.run_ro_queries (queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}], get_col_idx = False) 142 143 if len(rows) == 0: 144 raise gmExceptions.NoSuchBusinessObjectError('cannot retrieve data for template pk = %s' % self.pk_obj) 145 146 return rows[0][0]
147 148 template_data = property(_get_template_data, lambda x:x) 149 #--------------------------------------------------------
150 - def export_to_file(self, filename=None, chunksize=0):
151 """Export form template from database into file.""" 152 153 if filename is None: 154 if self._payload[self._idx['filename']] is None: 155 suffix = self.__class__._suffix4engine[self._payload[self._idx['engine']]] 156 else: 157 suffix = os.path.splitext(self._payload[self._idx['filename']].strip())[1].strip() 158 if suffix in [u'', u'.']: 159 suffix = self.__class__._suffix4engine[self._payload[self._idx['engine']]] 160 161 filename = gmTools.get_unique_filename ( 162 prefix = 'gm-%s-Template-' % self._payload[self._idx['engine']], 163 suffix = suffix 164 ) 165 166 data_query = { 167 'cmd': u'SELECT substring(data from %(start)s for %(size)s) FROM ref.paperwork_templates WHERE pk = %(pk)s', 168 'args': {'pk': self.pk_obj} 169 } 170 171 data_size_query = { 172 'cmd': u'select octet_length(data) from ref.paperwork_templates where pk = %(pk)s', 173 'args': {'pk': self.pk_obj} 174 } 175 176 result = gmPG2.bytea2file ( 177 data_query = data_query, 178 filename = filename, 179 data_size_query = data_size_query, 180 chunk_size = chunksize 181 ) 182 if result is False: 183 return None 184 185 return filename
186 #--------------------------------------------------------
187 - def update_template_from_file(self, filename=None):
188 gmPG2.file2bytea ( 189 filename = filename, 190 query = u'update ref.paperwork_templates set data = %(data)s::bytea where pk = %(pk)s and xmin = %(xmin)s', 191 args = {'pk': self.pk_obj, 'xmin': self._payload[self._idx['xmin_paperwork_template']]} 192 ) 193 # adjust for xmin change 194 self.refetch_payload()
195 #--------------------------------------------------------
196 - def instantiate(self):
197 fname = self.export_to_file() 198 engine = form_engines[self._payload[self._idx['engine']]] 199 return engine(template_file = fname)
200 #============================================================
201 -def get_form_template(name_long=None, external_version=None):
202 cmd = u'select pk from ref.paperwork_templates where name_long = %(lname)s and external_version = %(ver)s' 203 args = {'lname': name_long, 'ver': external_version} 204 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 205 206 if len(rows) == 0: 207 _log.error('cannot load form template [%s - %s]', name_long, external_version) 208 return None 209 210 return cFormTemplate(aPK_obj = rows[0]['pk'])
211 #------------------------------------------------------------
212 -def get_form_templates(engine=None, active_only=False):
213 """Load form templates.""" 214 215 args = {'eng': engine, 'in_use': active_only} 216 217 where_parts = [] 218 if engine is not None: 219 where_parts.append(u'engine = %(eng)s') 220 221 if active_only: 222 where_parts.append(u'in_use is True') 223 224 if len(where_parts) == 0: 225 cmd = u"select * from ref.v_paperwork_templates order by in_use desc, name_long" 226 else: 227 cmd = u"select * from ref.v_paperwork_templates where %s order by in_use desc, name_long" % u'and'.join(where_parts) 228 229 rows, idx = gmPG2.run_ro_queries ( 230 queries = [{'cmd': cmd, 'args': args}], 231 get_col_idx = True 232 ) 233 templates = [ cFormTemplate(row = {'pk_field': 'pk_paperwork_template', 'data': r, 'idx': idx}) for r in rows ] 234 235 return templates
236 #------------------------------------------------------------
237 -def create_form_template(template_type=None, name_short=None, name_long=None):
238 239 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)' 240 rows, idx = gmPG2.run_rw_queries ( 241 queries = [ 242 {'cmd': cmd, 'args': {'type': template_type, 'nshort': name_short, 'nlong': name_long, 'ext_version': 'new'}}, 243 {'cmd': u"select currval(pg_get_serial_sequence('ref.paperwork_templates', 'pk'))"} 244 ], 245 return_data = True 246 ) 247 template = cFormTemplate(aPK_obj = rows[0][0]) 248 return template
249 #------------------------------------------------------------
250 -def delete_form_template(template=None):
251 rows, idx = gmPG2.run_rw_queries ( 252 queries = [ 253 {'cmd': u'delete from ref.paperwork_templates where pk=%(pk)s', 'args': {'pk': template['pk_paperwork_template']}} 254 ] 255 ) 256 return True
257 #============================================================ 258 # OpenOffice API 259 #============================================================ 260 uno = None 261 cOOoDocumentCloseListener = None 262 263 #-----------------------------------------------------------
264 -def __configure_path_to_UNO():
265 266 try: 267 which = subprocess.Popen ( 268 args = ('which', 'soffice'), 269 stdout = subprocess.PIPE, 270 stdin = subprocess.PIPE, 271 stderr = subprocess.PIPE, 272 universal_newlines = True 273 ) 274 except (OSError, ValueError, subprocess.CalledProcessError): 275 _log.exception('there was a problem executing [which soffice]') 276 return 277 278 soffice_path, err = which.communicate() 279 soffice_path = soffice_path.strip('\n') 280 uno_path = os.path.abspath ( os.path.join ( 281 os.path.dirname(os.path.realpath(soffice_path)), 282 '..', 283 'basis-link', 284 'program' 285 )) 286 287 _log.info('UNO should be at [%s], appending to sys.path', uno_path) 288 289 sys.path.append(uno_path)
290 #-----------------------------------------------------------
291 -def init_ooo():
292 """FIXME: consider this: 293 294 try: 295 import uno 296 except: 297 print "This Script needs to be run with the python from OpenOffice.org" 298 print "Example: /opt/OpenOffice.org/program/python %s" % ( 299 os.path.basename(sys.argv[0])) 300 print "Or you need to insert the right path at the top, where uno.py is." 301 print "Default: %s" % default_path 302 """ 303 global uno 304 if uno is not None: 305 return 306 307 try: 308 import uno 309 except ImportError: 310 __configure_path_to_UNO() 311 import uno 312 313 global unohelper, oooXCloseListener, oooNoConnectException, oooPropertyValue 314 315 import unohelper 316 from com.sun.star.util import XCloseListener as oooXCloseListener 317 from com.sun.star.connection import NoConnectException as oooNoConnectException 318 from com.sun.star.beans import PropertyValue as oooPropertyValue 319 320 #---------------------------------- 321 class _cOOoDocumentCloseListener(unohelper.Base, oooXCloseListener): 322 """Listens for events sent by OOo during the document closing 323 sequence and notifies the GNUmed client GUI so it can 324 import the closed document into the database. 325 """ 326 def __init__(self, document=None): 327 self.document = document
328 329 def queryClosing(self, evt, owner): 330 # owner is True/False whether I am the owner of the doc 331 pass 332 333 def notifyClosing(self, evt): 334 pass 335 336 def disposing(self, evt): 337 self.document.on_disposed_by_ooo() 338 self.document = None 339 #---------------------------------- 340 341 global cOOoDocumentCloseListener 342 cOOoDocumentCloseListener = _cOOoDocumentCloseListener 343 344 _log.debug('python UNO bridge successfully initialized') 345 346 #------------------------------------------------------------
347 -class gmOOoConnector(gmBorg.cBorg):
348 """This class handles the connection to OOo. 349 350 Its Singleton instance stays around once initialized. 351 """ 352 # FIXME: need to detect closure of OOo !
353 - def __init__(self):
354 355 init_ooo() 356 357 #self.ooo_start_cmd = 'oowriter -invisible -accept="socket,host=localhost,port=2002;urp;"' 358 #self.remote_context_uri = "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" 359 360 pipe_name = "uno-gm2ooo-%s" % str(random.random())[2:] 361 self.ooo_start_cmd = 'oowriter -invisible -norestore -accept="pipe,name=%s;urp"' % pipe_name 362 self.remote_context_uri = "uno:pipe,name=%s;urp;StarOffice.ComponentContext" % pipe_name 363 364 _log.debug('pipe name: %s', pipe_name) 365 _log.debug('startup command: %s', self.ooo_start_cmd) 366 _log.debug('remote context URI: %s', self.remote_context_uri) 367 368 self.resolver_uri = "com.sun.star.bridge.UnoUrlResolver" 369 self.desktop_uri = "com.sun.star.frame.Desktop" 370 371 self.local_context = uno.getComponentContext() 372 self.uri_resolver = self.local_context.ServiceManager.createInstanceWithContext(self.resolver_uri, self.local_context) 373 374 self.__desktop = None
375 #--------------------------------------------------------
376 - def cleanup(self, force=True):
377 if self.__desktop is None: 378 _log.debug('no desktop, no cleanup') 379 return 380 381 try: 382 self.__desktop.terminate() 383 except: 384 _log.exception('cannot terminate OOo desktop')
385 #--------------------------------------------------------
386 - def open_document(self, filename=None):
387 """<filename> must be absolute""" 388 389 if self.desktop is None: 390 _log.error('cannot access OOo desktop') 391 return None 392 393 filename = os.path.expanduser(filename) 394 filename = os.path.abspath(filename) 395 document_uri = uno.systemPathToFileUrl(filename) 396 397 _log.debug('%s -> %s', filename, document_uri) 398 399 doc = self.desktop.loadComponentFromURL(document_uri, "_blank", 0, ()) 400 return doc
401 #-------------------------------------------------------- 402 # internal helpers 403 #--------------------------------------------------------
404 - def __get_startup_settle_time(self):
405 # later factor this out ! 406 dbcfg = gmCfg.cCfgSQL() 407 self.ooo_startup_settle_time = dbcfg.get2 ( 408 option = u'external.ooo.startup_settle_time', 409 workplace = gmSurgery.gmCurrentPractice().active_workplace, 410 bias = u'workplace', 411 default = 3.0 412 )
413 #-------------------------------------------------------- 414 # properties 415 #--------------------------------------------------------
416 - def _get_desktop(self):
417 if self.__desktop is not None: 418 return self.__desktop 419 420 try: 421 self.remote_context = self.uri_resolver.resolve(self.remote_context_uri) 422 except oooNoConnectException: 423 _log.exception('cannot connect to OOo server') 424 _log.info('trying to start OOo server') 425 os.system(self.ooo_start_cmd) 426 self.__get_startup_settle_time() 427 _log.debug('waiting %s seconds for OOo to start up', self.ooo_startup_settle_time) 428 time.sleep(self.ooo_startup_settle_time) # OOo sometimes needs a bit 429 try: 430 self.remote_context = self.uri_resolver.resolve(self.remote_context_uri) 431 except oooNoConnectException: 432 _log.exception('cannot start (or connect to started) OOo server') 433 return None 434 435 self.__desktop = self.remote_context.ServiceManager.createInstanceWithContext(self.desktop_uri, self.remote_context) 436 _log.debug('connection seems established') 437 return self.__desktop
438 439 desktop = property(_get_desktop, lambda x:x)
440 #------------------------------------------------------------
441 -class cOOoLetter(object):
442
443 - def __init__(self, template_file=None, instance_type=None):
444 445 self.template_file = template_file 446 self.instance_type = instance_type 447 self.ooo_doc = None
448 #-------------------------------------------------------- 449 # external API 450 #--------------------------------------------------------
451 - def open_in_ooo(self):
452 # connect to OOo 453 ooo_srv = gmOOoConnector() 454 455 # open doc in OOo 456 self.ooo_doc = ooo_srv.open_document(filename = self.template_file) 457 if self.ooo_doc is None: 458 _log.error('cannot open document in OOo') 459 return False 460 461 # listen for close events 462 pat = gmPerson.gmCurrentPatient() 463 pat.locked = True 464 listener = cOOoDocumentCloseListener(document = self) 465 self.ooo_doc.addCloseListener(listener) 466 467 return True
468 #--------------------------------------------------------
469 - def show(self, visible=True):
470 self.ooo_doc.CurrentController.Frame.ContainerWindow.setVisible(visible)
471 #--------------------------------------------------------
472 - def replace_placeholders(self, handler=None, old_style_too = True):
473 474 # new style embedded, implicit placeholders 475 searcher = self.ooo_doc.createSearchDescriptor() 476 searcher.SearchCaseSensitive = False 477 searcher.SearchRegularExpression = True 478 searcher.SearchWords = True 479 searcher.SearchString = handler.placeholder_regex 480 481 placeholder_instance = self.ooo_doc.findFirst(searcher) 482 while placeholder_instance is not None: 483 try: 484 val = handler[placeholder_instance.String] 485 except: 486 _log.exception(val) 487 val = _('error with placeholder [%s]') % placeholder_instance.String 488 489 if val is None: 490 val = _('error with placeholder [%s]') % placeholder_instance.String 491 492 placeholder_instance.String = val 493 placeholder_instance = self.ooo_doc.findNext(placeholder_instance.End, searcher) 494 495 if not old_style_too: 496 return 497 498 # old style "explicit" placeholders 499 text_fields = self.ooo_doc.getTextFields().createEnumeration() 500 while text_fields.hasMoreElements(): 501 text_field = text_fields.nextElement() 502 503 # placeholder ? 504 if not text_field.supportsService('com.sun.star.text.TextField.JumpEdit'): 505 continue 506 # placeholder of type text ? 507 if text_field.PlaceHolderType != 0: 508 continue 509 510 replacement = handler[text_field.PlaceHolder] 511 if replacement is None: 512 continue 513 514 text_field.Anchor.setString(replacement)
515 #--------------------------------------------------------
516 - def save_in_ooo(self, filename=None):
517 if filename is not None: 518 target_url = uno.systemPathToFileUrl(os.path.abspath(os.path.expanduser(filename))) 519 save_args = ( 520 oooPropertyValue('Overwrite', 0, True, 0), 521 oooPropertyValue('FormatFilter', 0, 'swriter: StarOffice XML (Writer)', 0) 522 523 ) 524 # "store AS url" stores the doc, marks it unmodified and updates 525 # the internal media descriptor - as opposed to "store TO url" 526 self.ooo_doc.storeAsURL(target_url, save_args) 527 else: 528 self.ooo_doc.store()
529 #--------------------------------------------------------
530 - def close_in_ooo(self):
531 self.ooo_doc.dispose() 532 pat = gmPerson.gmCurrentPatient() 533 pat.locked = False 534 self.ooo_doc = None
535 #--------------------------------------------------------
536 - def on_disposed_by_ooo(self):
537 # get current file name from OOo, user may have used Save As 538 filename = uno.fileUrlToSystemPath(self.ooo_doc.URL) 539 # tell UI to import the file 540 gmDispatcher.send ( 541 signal = u'import_document_from_file', 542 filename = filename, 543 document_type = self.instance_type, 544 unlock_patient = True 545 ) 546 self.ooo_doc = None
547 #-------------------------------------------------------- 548 # internal helpers 549 #-------------------------------------------------------- 550 551 #============================================================
552 -class cFormEngine(object):
553 """Ancestor for forms.""" 554
555 - def __init__ (self, template_file=None):
556 self.template_filename = template_file
557 #--------------------------------------------------------
558 - def substitute_placeholders(self, data_source=None):
559 """Parse the template into an instance and replace placeholders with values.""" 560 raise NotImplementedError
561 #--------------------------------------------------------
562 - def edit(self):
563 """Allow editing the instance of the template.""" 564 raise NotImplementedError
565 #--------------------------------------------------------
566 - def generate_output(self, format=None):
567 """Generate output suitable for further processing outside this class, e.g. printing.""" 568 raise NotImplementedError
569 #--------------------------------------------------------
570 - def process (self, data_source=None):
571 """Merge values into the form template. 572 """ 573 pass
574 #--------------------------------------------------------
575 - def cleanup (self):
576 """ 577 A sop to TeX which can't act as a true filter: to delete temporary files 578 """ 579 pass
580 #--------------------------------------------------------
581 - def exe (self, command):
582 """ 583 Executes the provided command. 584 If command cotains %F. it is substituted with the filename 585 Otherwise, the file is fed in on stdin 586 """ 587 pass
588 #--------------------------------------------------------
589 - def store(self, params=None):
590 """Stores the parameters in the backend. 591 592 - link_obj can be a cursor, a connection or a service name 593 - assigning a cursor to link_obj allows the calling code to 594 group the call to store() into an enclosing transaction 595 (for an example see gmReferral.send_referral()...) 596 """ 597 # some forms may not have values ... 598 if params is None: 599 params = {} 600 patient_clinical = self.patient.get_emr() 601 encounter = patient_clinical.active_encounter['pk_encounter'] 602 # FIXME: get_active_episode is no more 603 #episode = patient_clinical.get_active_episode()['pk_episode'] 604 # generate "forever unique" name 605 cmd = "select name_short || ': <' || name_long || '::' || external_version || '>' from paperwork_templates where pk=%s"; 606 rows = gmPG.run_ro_query('reference', cmd, None, self.pk_def) 607 form_name = None 608 if rows is None: 609 _log.error('error retrieving form def for [%s]' % self.pk_def) 610 elif len(rows) == 0: 611 _log.error('no form def for [%s]' % self.pk_def) 612 else: 613 form_name = rows[0][0] 614 # we didn't get a name but want to store the form anyhow 615 if form_name is None: 616 form_name=time.time() # hopefully unique enough 617 # in one transaction 618 queries = [] 619 # - store form instance in form_instance 620 cmd = "insert into form_instances(fk_form_def, form_name, fk_episode, fk_encounter) values (%s, %s, %s, %s)" 621 queries.append((cmd, [self.pk_def, form_name, episode, encounter])) 622 # - store params in form_data 623 for key in params.keys(): 624 cmd = """ 625 insert into form_data(fk_instance, place_holder, value) 626 values ((select currval('form_instances_pk_seq')), %s, %s::text) 627 """ 628 queries.append((cmd, [key, params[key]])) 629 # - get inserted PK 630 queries.append(("select currval ('form_instances_pk_seq')", [])) 631 status, err = gmPG.run_commit('historica', queries, True) 632 if status is None: 633 _log.error('failed to store form [%s] (%s): %s' % (self.pk_def, form_name, err)) 634 return None 635 return status
636 637 #================================================================ 638 # OOo template forms 639 #----------------------------------------------------------------
640 -class cOOoForm(cFormEngine):
641 """A forms engine wrapping OOo.""" 642
643 - def __init__ (self, template_file=None):
644 super(self.__class__, self).__init__(template_file = template_file) 645 646 647 path, ext = os.path.splitext(self.template_filename) 648 if ext in [r'', r'.']: 649 ext = r'.odt' 650 self.instance_filename = r'%s-instance%s' % (path, ext)
651 652 #================================================================ 653 # LaTeX template forms 654 #----------------------------------------------------------------
655 -class cLaTeXForm(cFormEngine):
656 """A forms engine wrapping LaTeX.""" 657
658 - def __init__ (self, template_file=None):
659 super(self.__class__, self).__init__(template_file = template_file) 660 path, ext = os.path.splitext(self.template_filename) 661 if ext in [r'', r'.']: 662 ext = r'.tex' 663 self.instance_filename = r'%s-instance%s' % (path, ext)
664 #--------------------------------------------------------
665 - def substitute_placeholders(self, data_source=None):
666 667 template_file = codecs.open(self.template_filename, 'rU', 'utf8') 668 instance_file = codecs.open(self.instance_filename, 'wb', 'utf8') 669 670 for line in template_file: 671 672 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 673 instance_file.write(line) 674 continue 675 676 # 1) find placeholders in this line 677 placeholders_in_line = regex.findall(data_source.placeholder_regex, line, regex.IGNORECASE) 678 # 2) and replace them 679 for placeholder in placeholders_in_line: 680 #line = line.replace(placeholder, self._texify_string(data_source[placeholder])) 681 try: 682 val = data_source[placeholder] 683 except: 684 _log.exception(val) 685 val = _('error with placeholder [%s]') % placeholder 686 687 if val is None: 688 val = _('error with placeholder [%s]') % placeholder 689 690 line = line.replace(placeholder, val) 691 692 instance_file.write(line) 693 694 instance_file.close() 695 template_file.close() 696 697 return
698 #--------------------------------------------------------
699 - def edit(self):
700 701 mimetypes = [ 702 u'application/x-latex', 703 u'application/x-tex', 704 u'text/plain' 705 ] 706 707 for mimetype in mimetypes: 708 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename) 709 710 if editor_cmd is None: 711 editor_cmd = u'sensible-editor %s' % self.instance_filename 712 713 return gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True)
714 #--------------------------------------------------------
715 - def generate_output(self, instance_file = None, format=None, cleanup=True):
716 717 if instance_file is None: 718 instance_file = self.instance_filename 719 720 try: 721 open(instance_file, 'r').close() 722 except: 723 _log.exception('cannot access form instance file [%s]', instance_file) 724 gmLog2.log_stack_trace() 725 return None 726 727 self.instance_filename = instance_file 728 729 _log.debug('ignoring <format> directive [%s], generating PDF', format) 730 731 # create sandbox for LaTeX to play in 732 sandbox_dir = os.path.splitext(self.template_filename)[0] 733 _log.debug('LaTeX sandbox directory: [%s]', sandbox_dir) 734 735 old_cwd = os.getcwd() 736 _log.debug('CWD: [%s]', old_cwd) 737 738 gmTools.mkdir(sandbox_dir) 739 os.chdir(sandbox_dir) 740 741 sandboxed_instance_filename = os.path.join(sandbox_dir, os.path.split(self.instance_filename)[1]) 742 shutil.move(self.instance_filename, sandboxed_instance_filename) 743 744 # LaTeX can need up to three runs to get cross-references et al right 745 if platform.system() == 'Windows': 746 cmd = r'pdflatex.exe -interaction nonstopmode %s' % sandboxed_instance_filename 747 else: 748 cmd = r'pdflatex -interaction nonstopmode %s' % sandboxed_instance_filename 749 for run in [1, 2, 3]: 750 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True): 751 _log.error('problem running pdflatex, cannot generate form output') 752 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdflatex. Cannot turn LaTeX template into PDF.'), beep = True) 753 return None 754 755 os.chdir(old_cwd) 756 pdf_name = u'%s.pdf' % os.path.splitext(sandboxed_instance_filename)[0] 757 shutil.move(pdf_name, os.path.split(self.instance_filename)[0]) 758 pdf_name = u'%s.pdf' % os.path.splitext(self.instance_filename)[0] 759 760 # cleanup LaTeX sandbox ? 761 if cleanup: 762 for fname in os.listdir(sandbox_dir): 763 os.remove(os.path.join(sandbox_dir, fname)) 764 os.rmdir(sandbox_dir) 765 766 try: 767 open(pdf_name, 'r').close() 768 return pdf_name 769 except IOError: 770 _log.exception('cannot open target PDF: %s', pdf_name) 771 772 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True) 773 return None
774 #--------------------------------------------------------
775 - def cleanup(self):
776 try: 777 os.remove(self.template_filename) 778 except: 779 _log.debug(u'cannot remove template file [%s]', self.template_filename)
780 #------------------------------------------------------------ 781 form_engines[u'L'] = cLaTeXForm 782 #============================================================ 783 # Gnuplot template forms 784 #------------------------------------------------------------
785 -class cGnuplotForm(cFormEngine):
786 """A forms engine wrapping Gnuplot.""" 787 788 #--------------------------------------------------------
789 - def substitute_placeholders(self, data_source=None):
790 """Parse the template into an instance and replace placeholders with values.""" 791 pass
792 #--------------------------------------------------------
793 - def edit(self):
794 """Allow editing the instance of the template.""" 795 pass
796 #--------------------------------------------------------
797 - def generate_output(self, format=None):
798 """Generate output suitable for further processing outside this class, e.g. printing. 799 800 Expects .data_filename to be set. 801 """ 802 self.conf_filename = gmTools.get_unique_filename(prefix = 'gm2gpl-', suffix = '.conf') 803 fname_file = codecs.open(self.conf_filename, 'wb', 'utf8') 804 fname_file.write('# setting the gnuplot data file\n') 805 fname_file.write("gm2gpl_datafile = '%s'\n" % self.data_filename) 806 fname_file.close() 807 808 # FIXME: cater for configurable path 809 if platform.system() == 'Windows': 810 exec_name = 'gnuplot.exe' 811 else: 812 exec_name = 'gnuplot' 813 814 args = [exec_name, '-p', self.conf_filename, self.template_filename] 815 _log.debug('plotting args: %s' % str(args)) 816 817 try: 818 gp = subprocess.Popen ( 819 args = args, 820 close_fds = True 821 ) 822 except (OSError, ValueError, subprocess.CalledProcessError): 823 _log.exception('there was a problem executing gnuplot') 824 gmDispatcher.send(signal = u'statustext', msg = _('Error running gnuplot. Cannot plot data.'), beep = True) 825 return 826 827 gp.communicate() 828 829 return
830 #--------------------------------------------------------
831 - def cleanup (self):
832 try: 833 os.remove(self.template_filename) 834 os.remove(self.conf_filename) 835 os.remove(self.data_filename) 836 except StandardError: 837 _log.exception(u'cannot remove either of script/conf/data file')
838 #------------------------------------------------------------ 839 form_engines[u'G'] = cGnuplotForm 840 #------------------------------------------------------------ 841 #------------------------------------------------------------
842 -class cIanLaTeXForm(cFormEngine):
843 """A forms engine wrapping LaTeX. 844 """
845 - def __init__ (self, id, template):
846 self.id = id 847 self.template = template
848
849 - def process (self,params={}):
850 try: 851 latex = Cheetah.Template.Template (self.template, filter=LaTeXFilter, searchList=[params]) 852 # create a 'sandbox' directory for LaTeX to play in 853 self.tmp = tempfile.mktemp () 854 os.makedirs (self.tmp) 855 self.oldcwd = os.getcwd () 856 os.chdir (self.tmp) 857 stdin = os.popen ("latex", "w", 2048) 858 stdin.write (str (latex)) #send text. LaTeX spits it's output into stdout 859 # FIXME: send LaTeX output to the logger 860 stdin.close () 861 if not gmShellAPI.run_command_in_shell("dvips texput.dvi -o texput.ps", blocking=True): 862 raise FormError ('DVIPS returned error') 863 except EnvironmentError, e: 864 _log.error(e.strerror) 865 raise FormError (e.strerror) 866 return file ("texput.ps")
867
868 - def xdvi (self):
869 """ 870 For testing purposes, runs Xdvi on the intermediate TeX output 871 WARNING: don't try this on Windows 872 """ 873 gmShellAPI.run_command_in_shell("xdvi texput.dvi", blocking=True)
874
875 - def exe (self, command):
876 if "%F" in command: 877 command.replace ("%F", "texput.ps") 878 else: 879 command = "%s < texput.ps" % command 880 try: 881 if not gmShellAPI.run_command_in_shell(command, blocking=True): 882 _log.error("external command %s returned non-zero" % command) 883 raise FormError ('external command %s returned error' % command) 884 except EnvironmentError, e: 885 _log.error(e.strerror) 886 raise FormError (e.strerror) 887 return True
888
889 - def printout (self):
890 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print') 891 self.exe (command)
892
893 - def cleanup (self):
894 """ 895 Delete all the LaTeX output iles 896 """ 897 for i in os.listdir ('.'): 898 os.unlink (i) 899 os.chdir (self.oldcwd) 900 os.rmdir (self.tmp)
901 902 903 904 905 #================================================================ 906 # define a class for HTML forms (for printing) 907 #================================================================
908 -class cXSLTFormEngine(cFormEngine):
909 """This class can create XML document from requested data, 910 then process it with XSLT template and display results 911 """ 912 913 # FIXME: make the path configurable ? 914 _preview_program = u'oowriter ' #this program must be in the system PATH 915
916 - def __init__ (self, template=None):
917 918 if template is None: 919 raise ValueError(u'%s: cannot create form instance without a template' % __name__) 920 921 cFormEngine.__init__(self, template = template) 922 923 self._FormData = None 924 925 # here we know/can assume that the template was stored as a utf-8 926 # encoded string so use that conversion to create unicode: 927 #self._XSLTData = unicode(str(template.template_data), 'UTF-8') 928 # but in fact, unicode() knows how to handle buffers, so simply: 929 self._XSLTData = unicode(self.template.template_data, 'UTF-8', 'strict') 930 931 # we must still devise a method of extracting the SQL query: 932 # - either by retrieving it from a particular tag in the XSLT or 933 # - by making the stored template actually be a dict which, unpickled, 934 # has the keys "xslt" and "sql" 935 self._SQL_query = u'select 1' #this sql query must output valid xml
936 #-------------------------------------------------------- 937 # external API 938 #--------------------------------------------------------
939 - def process(self, sql_parameters):
940 """get data from backend and process it with XSLT template to produce readable output""" 941 942 # extract SQL (this is wrong but displays what is intended) 943 xslt = libxml2.parseDoc(self._XSLTData) 944 root = xslt.children 945 for child in root: 946 if child.type == 'element': 947 self._SQL_query = child.content 948 break 949 950 # retrieve data from backend 951 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': self._SQL_query, 'args': sql_parameters}], get_col_idx = False) 952 953 __header = '<?xml version="1.0" encoding="UTF-8"?>\n' 954 __body = rows[0][0] 955 956 # process XML data according to supplied XSLT, producing HTML 957 self._XMLData =__header + __body 958 style = libxslt.parseStylesheetDoc(xslt) 959 xml = libxml2.parseDoc(self._XMLData) 960 html = style.applyStylesheet(xml, None) 961 self._FormData = html.serialize() 962 963 style.freeStylesheet() 964 xml.freeDoc() 965 html.freeDoc()
966 #--------------------------------------------------------
967 - def preview(self):
968 if self._FormData is None: 969 raise ValueError, u'Preview request for empty form. Make sure the form is properly initialized and process() was performed' 970 971 fname = gmTools.get_unique_filename(prefix = u'gm_XSLT_form-', suffix = u'.html') 972 #html_file = os.open(fname, 'wb') 973 #html_file.write(self._FormData.encode('UTF-8')) 974 html_file = codecs.open(fname, 'wb', 'utf8', 'strict') # or 'replace' ? 975 html_file.write(self._FormData) 976 html_file.close() 977 978 cmd = u'%s %s' % (self.__class__._preview_program, fname) 979 980 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = False): 981 _log.error('%s: cannot launch report preview program' % __name__) 982 return False 983 984 #os.unlink(self.filename) #delete file 985 #FIXME: under Windows the temp file is deleted before preview program gets it (under Linux it works OK) 986 987 return True
988 #--------------------------------------------------------
989 - def print_directly(self):
990 #not so fast, look at it first 991 self.preview()
992 993 994 #===================================================== 995 #class LaTeXFilter(Cheetah.Filters.Filter):
996 -class LaTeXFilter:
997 - def filter (self, item, table_sep= " \\\\\n", **kwds):
998 """ 999 Convience function to escape ISO-Latin-1 strings for TeX output 1000 WARNING: not all ISO-Latin-1 characters are expressible in TeX 1001 FIXME: nevertheless, there are a few more we could support 1002 1003 Also intelligently convert lists and tuples into TeX-style table lines 1004 """ 1005 if type (item) is types.UnicodeType or type (item) is types.StringType: 1006 item = item.replace ("\\", "\\backslash") # I wonder about this, do we want users to be able to use raw TeX? 1007 item = item.replace ("&", "\\&") 1008 item = item.replace ("$", "\\$") 1009 item = item.replace ('"', "") # okay, that's not right, but easiest solution for now 1010 item = item.replace ("\n", "\\\\ ") 1011 if len (item.strip ()) == 0: 1012 item = "\\relax " # sometimes TeX really hates empty strings, this seems to mollify it 1013 # FIXME: cover all of ISO-Latin-1 which can be expressed in TeX 1014 if type (item) is types.UnicodeType: 1015 item = item.encode ('latin-1', 'replace') 1016 trans = {'ß':'\\ss{}', 'ä': '\\"{a}', 'Ä' :'\\"{A}', 'ö': '\\"{o}', 'Ö': '\\"{O}', 'ü': '\\"{u}', 'Ü': '\\"{U}', 1017 '\x8a':'\\v{S}', '\x8a':'\\OE{}', '\x9a':'\\v{s}', '\x9c': '\\oe{}', '\a9f':'\\"{Y}', #Microsloth extensions 1018 '\x86': '{\\dag}', '\x87': '{\\ddag}', '\xa7':'{\\S}', '\xb6': '{\\P}', '\xa9': '{\\copyright}', '\xbf': '?`', 1019 '\xc0':'\\`{A}', '\xa1': "\\'{A}", '\xa2': '\\^{A}', '\xa3':'\\~{A}', '\\xc5': '{\AA}', 1020 '\xc7':'\\c{C}', '\xc8':'\\`{E}', 1021 '\xa1': '!`', 1022 '\xb5':'$\mu$', '\xa3': '\pounds{}', '\xa2':'cent'} 1023 for k, i in trans.items (): 1024 item = item.replace (k, i) 1025 elif type (item) is types.ListType or type (item) is types.TupleType: 1026 item = string.join ([self.filter (i, ' & ') for i in item], table_sep) 1027 elif item is None: 1028 item = '\\relax % Python None\n' 1029 elif type (item) is types.IntType or type (item) is types.FloatType: 1030 item = str (item) 1031 else: 1032 item = str (item) 1033 _log.warning("unknown type %s, string %s" % (type (item), item)) 1034 return item
1035 1036 1037 #===========================================================
1038 -class cHL7Form (cFormEngine):
1039 pass
1040 1041 #============================================================ 1042 # convenience functions 1043 #------------------------------------------------------------
1044 -def get_form(id):
1045 """ 1046 Instantiates a FormEngine based on the form ID or name from the backend 1047 """ 1048 try: 1049 # it's a number: match to form ID 1050 id = int (id) 1051 cmd = 'select template, engine, pk from paperwork_templates where pk = %s' 1052 except ValueError: 1053 # it's a string, match to the form's name 1054 # FIXME: can we somehow OR like this: where name_short=%s OR name_long=%s ? 1055 cmd = 'select template, engine, flags, pk from paperwork_templates where name_short = %s' 1056 result = gmPG.run_ro_query ('reference', cmd, None, id) 1057 if result is None: 1058 _log.error('error getting form [%s]' % id) 1059 raise gmExceptions.FormError ('error getting form [%s]' % id) 1060 if len(result) == 0: 1061 _log.error('no form [%s] found' % id) 1062 raise gmExceptions.FormError ('no such form found [%s]' % id) 1063 if result[0][1] == 'L': 1064 return LaTeXForm (result[0][2], result[0][0]) 1065 elif result[0][1] == 'T': 1066 return TextForm (result[0][2], result[0][0]) 1067 else: 1068 _log.error('no form engine [%s] for form [%s]' % (result[0][1], id)) 1069 raise FormError ('no engine [%s] for form [%s]' % (result[0][1], id))
1070 #-------------------------------------------------------------
1071 -class FormError (Exception):
1072 - def __init__ (self, value):
1073 self.value = value
1074
1075 - def __str__ (self):
1076 return repr (self.value)
1077 #------------------------------------------------------------- 1078 1079 test_letter = """ 1080 \\documentclass{letter} 1081 \\address{ $DOCTOR \\\\ 1082 $DOCTORADDRESS} 1083 \\signature{$DOCTOR} 1084 1085 \\begin{document} 1086 \\begin{letter}{$RECIPIENTNAME \\\\ 1087 $RECIPIENTADDRESS} 1088 1089 \\opening{Dear $RECIPIENTNAME} 1090 1091 \\textbf{Re:} $PATIENTNAME, DOB: $DOB, $PATIENTADDRESS \\\\ 1092 1093 $TEXT 1094 1095 \\ifnum$INCLUDEMEDS>0 1096 \\textbf{Medications List} 1097 1098 \\begin{tabular}{lll} 1099 $MEDSLIST 1100 \\end{tabular} 1101 \\fi 1102 1103 \\ifnum$INCLUDEDISEASES>0 1104 \\textbf{Disease List} 1105 1106 \\begin{tabular}{l} 1107 $DISEASELIST 1108 \\end{tabular} 1109 \\fi 1110 1111 \\closing{$CLOSING} 1112 1113 \\end{letter} 1114 \\end{document} 1115 """ 1116 1117
1118 -def test_au():
1119 f = open('../../test-area/ian/terry-form.tex') 1120 params = { 1121 'RECIPIENT': "Dr. R. Terry\n1 Main St\nNewcastle", 1122 'DOCTORSNAME': 'Ian Haywood', 1123 'DOCTORSADDRESS': '1 Smith St\nMelbourne', 1124 'PATIENTNAME':'Joe Bloggs', 1125 'PATIENTADDRESS':'18 Fred St\nMelbourne', 1126 'REQUEST':'echocardiogram', 1127 'THERAPY':'on warfarin', 1128 'CLINICALNOTES':"""heard new murmur 1129 Here's some 1130 crap to demonstrate how it can cover multiple lines.""", 1131 'COPYADDRESS':'Karsten Hilbert\nLeipzig, Germany', 1132 'ROUTINE':1, 1133 'URGENT':0, 1134 'FAX':1, 1135 'PHONE':1, 1136 'PENSIONER':1, 1137 'VETERAN':0, 1138 'PADS':0, 1139 'INSTRUCTIONS':u'Take the blue pill, Neo' 1140 } 1141 form = LaTeXForm (1, f.read()) 1142 form.process (params) 1143 form.xdvi () 1144 form.cleanup ()
1145
1146 -def test_au2 ():
1147 form = LaTeXForm (2, test_letter) 1148 params = {'RECIPIENTNAME':'Dr. Richard Terry', 1149 'RECIPIENTADDRESS':'1 Main St\nNewcastle', 1150 'DOCTOR':'Dr. Ian Haywood', 1151 'DOCTORADDRESS':'1 Smith St\nMelbourne', 1152 'PATIENTNAME':'Joe Bloggs', 1153 'PATIENTADDRESS':'18 Fred St, Melbourne', 1154 'TEXT':"""This is the main text of the referral letter""", 1155 'DOB':'12/3/65', 1156 'INCLUDEMEDS':1, 1157 'MEDSLIST':[["Amoxycillin", "500mg", "TDS"], ["Perindopril", "4mg", "OD"]], 1158 'INCLUDEDISEASES':0, 'DISEASELIST':'', 1159 'CLOSING':'Yours sincerely,' 1160 } 1161 form.process (params) 1162 print os.getcwd () 1163 form.xdvi () 1164 form.cleanup ()
1165 #------------------------------------------------------------
1166 -def test_de():
1167 template = open('../../test-area/ian/Formularkopf-DE.tex') 1168 form = LaTeXForm(template=template.read()) 1169 params = { 1170 'PATIENT LASTNAME': 'Kirk', 1171 'PATIENT FIRSTNAME': 'James T.', 1172 'PATIENT STREET': 'Hauptstrasse', 1173 'PATIENT ZIP': '02999', 1174 'PATIENT TOWN': 'Gross Saerchen', 1175 'PATIENT DOB': '22.03.1931' 1176 } 1177 form.process(params) 1178 form.xdvi() 1179 form.cleanup()
1180 1181 #============================================================ 1182 # main 1183 #------------------------------------------------------------ 1184 if __name__ == '__main__': 1185 1186 if len(sys.argv) < 2: 1187 sys.exit() 1188 1189 if sys.argv[1] != 'test': 1190 sys.exit() 1191 1192 from Gnumed.pycommon import gmI18N, gmDateTime 1193 gmI18N.activate_locale() 1194 gmI18N.install_domain(domain='gnumed') 1195 gmDateTime.init() 1196 1197 #-------------------------------------------------------- 1198 # OOo 1199 #--------------------------------------------------------
1200 - def test_init_ooo():
1201 init_ooo()
1202 #--------------------------------------------------------
1203 - def test_ooo_connect():
1204 srv = gmOOoConnector() 1205 print srv 1206 print srv.desktop
1207 #--------------------------------------------------------
1208 - def test_open_ooo_doc_from_srv():
1209 srv = gmOOoConnector() 1210 doc = srv.open_document(filename = sys.argv[2]) 1211 print "document:", doc
1212 #--------------------------------------------------------
1213 - def test_open_ooo_doc_from_letter():
1214 doc = cOOoLetter(template_file = sys.argv[2]) 1215 doc.open_in_ooo() 1216 print "document:", doc 1217 raw_input('press <ENTER> to continue') 1218 doc.show() 1219 #doc.replace_placeholders() 1220 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1221 # doc = None 1222 # doc.close_in_ooo() 1223 raw_input('press <ENTER> to continue')
1224 #--------------------------------------------------------
1225 - def play_with_ooo():
1226 try: 1227 doc = open_uri_in_ooo(filename=sys.argv[1]) 1228 except: 1229 _log.exception('cannot open [%s] in OOo' % sys.argv[1]) 1230 raise 1231 1232 class myCloseListener(unohelper.Base, oooXCloseListener): 1233 def disposing(self, evt): 1234 print "disposing:"
1235 def notifyClosing(self, evt): 1236 print "notifyClosing:" 1237 def queryClosing(self, evt, owner): 1238 # owner is True/False whether I am the owner of the doc 1239 print "queryClosing:" 1240 1241 l = myCloseListener() 1242 doc.addCloseListener(l) 1243 1244 tfs = doc.getTextFields().createEnumeration() 1245 print tfs 1246 print dir(tfs) 1247 while tfs.hasMoreElements(): 1248 tf = tfs.nextElement() 1249 if tf.supportsService('com.sun.star.text.TextField.JumpEdit'): 1250 print tf.getPropertyValue('PlaceHolder') 1251 print " ", tf.getPropertyValue('Hint') 1252 1253 # doc.close(True) # closes but leaves open the dedicated OOo window 1254 doc.dispose() # closes and disposes of the OOo window 1255 #--------------------------------------------------------
1256 - def test_cOOoLetter():
1257 pat = gmPerson.ask_for_patient() 1258 if pat is None: 1259 return 1260 gmPerson.set_active_patient(patient = pat) 1261 1262 doc = cOOoLetter(template_file = sys.argv[2]) 1263 doc.open_in_ooo() 1264 print doc 1265 doc.show() 1266 #doc.replace_placeholders() 1267 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1268 doc = None 1269 # doc.close_in_ooo() 1270 raw_input('press <ENTER> to continue')
1271 #-------------------------------------------------------- 1272 # other 1273 #--------------------------------------------------------
1274 - def test_cFormTemplate():
1275 template = cFormTemplate(aPK_obj = sys.argv[2]) 1276 print template 1277 print template.export_to_file()
1278 #--------------------------------------------------------
1279 - def set_template_from_file():
1280 template = cFormTemplate(aPK_obj = sys.argv[2]) 1281 template.update_template_from_file(filename = sys.argv[3])
1282 #--------------------------------------------------------
1283 - def test_latex_form():
1284 pat = gmPerson.ask_for_patient() 1285 if pat is None: 1286 return 1287 gmPerson.set_active_patient(patient = pat) 1288 1289 gmPerson.gmCurrentProvider(provider = gmPerson.cStaff()) 1290 1291 path = os.path.abspath(sys.argv[2]) 1292 form = cLaTeXForm(template_file = path) 1293 1294 from Gnumed.wxpython import gmMacro 1295 ph = gmMacro.gmPlaceholderHandler() 1296 ph.debug = True 1297 instance_file = form.substitute_placeholders(data_source = ph) 1298 pdf_name = form.generate_output(instance_file = instance_file, cleanup = False) 1299 print "final PDF file is:", pdf_name
1300 1301 #-------------------------------------------------------- 1302 #-------------------------------------------------------- 1303 # now run the tests 1304 #test_au() 1305 #test_de() 1306 1307 # OOo 1308 #test_init_ooo() 1309 #test_ooo_connect() 1310 #test_open_ooo_doc_from_srv() 1311 #test_open_ooo_doc_from_letter() 1312 #play_with_ooo() 1313 #test_cOOoLetter() 1314 1315 #test_cFormTemplate() 1316 #set_template_from_file() 1317 test_latex_form() 1318 1319 #============================================================ 1320