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