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