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 v2 or later 
   7  """ 
   8  #============================================================ 
   9  __author__ ="Ian Haywood <ihaywood@gnu.org>, karsten.hilbert@gmx.net" 
  10   
  11   
  12  import os, sys, time, os.path, logging 
  13  import codecs 
  14  import re as regex 
  15  import shutil 
  16  import random, platform, subprocess 
  17  import socket                                                                           # needed for OOo on Windows 
  18  #, libxml2, libxslt 
  19  import shlex 
  20   
  21   
  22  if __name__ == '__main__': 
  23          sys.path.insert(0, '../../') 
  24          from Gnumed.pycommon import gmI18N 
  25          gmI18N.activate_locale() 
  26          gmI18N.install_domain(domain='gnumed') 
  27  from Gnumed.pycommon import gmTools 
  28  from Gnumed.pycommon import gmDispatcher 
  29  from Gnumed.pycommon import gmExceptions 
  30  from Gnumed.pycommon import gmMatchProvider 
  31  from Gnumed.pycommon import gmBorg 
  32  from Gnumed.pycommon import gmLog2 
  33  from Gnumed.pycommon import gmMimeLib 
  34  from Gnumed.pycommon import gmShellAPI 
  35  from Gnumed.pycommon import gmCfg 
  36  from Gnumed.pycommon import gmBusinessDBObject 
  37  from Gnumed.pycommon import gmPG2 
  38   
  39  from Gnumed.business import gmPerson 
  40  from Gnumed.business import gmStaff 
  41  from Gnumed.business import gmPersonSearch 
  42  from Gnumed.business import gmSurgery 
  43   
  44   
  45  _log = logging.getLogger('gm.forms') 
  46   
  47  #============================================================ 
  48  # this order is also used in choice boxes for the engine 
  49  form_engine_abbrevs = [u'O', u'L', u'I', u'G', u'P', u'A', u'X'] 
  50   
  51  form_engine_names = { 
  52          u'O': 'OpenOffice', 
  53          u'L': 'LaTeX', 
  54          u'I': 'Image editor', 
  55          u'G': 'Gnuplot script', 
  56          u'P': 'PDF forms', 
  57          u'A': 'AbiWord', 
  58          u'X': 'Xe(La)TeX' 
  59  } 
  60   
  61  form_engine_template_wildcards = { 
  62          u'O': u'*.o?t', 
  63          u'L': u'*.tex', 
  64          u'G': u'*.gpl', 
  65          u'P': u'*.pdf', 
  66          u'A': u'*.abw', 
  67          u'X': u'*.tex' 
  68  } 
  69   
  70  # is filled in further below after each engine is defined 
  71  form_engines = {} 
  72   
  73  #============================================================ 
  74  # match providers 
  75  #============================================================ 
76 -class cFormTemplateNameLong_MatchProvider(gmMatchProvider.cMatchProvider_SQL2):
77
78 - def __init__(self):
79 80 query = u""" 81 SELECT 82 name_long AS data, 83 name_long AS list_label, 84 name_long AS field_label 85 FROM ref.v_paperwork_templates 86 WHERE name_long %(fragment_condition)s 87 ORDER BY list_label 88 """ 89 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
90 #============================================================
91 -class cFormTemplateNameShort_MatchProvider(gmMatchProvider.cMatchProvider_SQL2):
92
93 - def __init__(self):
94 95 query = u""" 96 SELECT 97 name_short AS data, 98 name_short AS list_label, 99 name_short AS field_label 100 FROM ref.v_paperwork_templates 101 WHERE name_short %(fragment_condition)s 102 ORDER BY name_short 103 """ 104 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
105 #============================================================
106 -class cFormTemplateType_MatchProvider(gmMatchProvider.cMatchProvider_SQL2):
107
108 - def __init__(self):
109 110 query = u""" 111 SELECT DISTINCT ON (list_label) 112 pk AS data, 113 _(name) || ' (' || name || ')' AS list_label, 114 _(name) AS field_label 115 FROM ref.form_types 116 WHERE 117 _(name) %(fragment_condition)s 118 OR 119 name %(fragment_condition)s 120 ORDER BY list_label 121 """ 122 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = [query])
123 #============================================================
124 -class cFormTemplate(gmBusinessDBObject.cBusinessDBObject):
125 126 _cmd_fetch_payload = u'select * from ref.v_paperwork_templates where pk_paperwork_template = %s' 127 128 _cmds_store_payload = [ 129 u"""update ref.paperwork_templates set 130 name_short = %(name_short)s, 131 name_long = %(name_long)s, 132 fk_template_type = %(pk_template_type)s, 133 instance_type = %(instance_type)s, 134 engine = %(engine)s, 135 in_use = %(in_use)s, 136 filename = %(filename)s, 137 external_version = %(external_version)s 138 where 139 pk = %(pk_paperwork_template)s and 140 xmin = %(xmin_paperwork_template)s 141 """, 142 u"""select xmin_paperwork_template from ref.v_paperwork_templates where pk_paperwork_template = %(pk_paperwork_template)s""" 143 ] 144 145 _updatable_fields = [ 146 u'name_short', 147 u'name_long', 148 u'external_version', 149 u'pk_template_type', 150 u'instance_type', 151 u'engine', 152 u'in_use', 153 u'filename' 154 ] 155 156 _suffix4engine = { 157 u'O': u'.ott', 158 u'L': u'.tex', 159 u'T': u'.txt', 160 u'X': u'.xslt', 161 u'I': u'.img', 162 u'P': u'.pdf' 163 } 164 165 #--------------------------------------------------------
166 - def _get_template_data(self):
167 """The template itself better not be arbitrarily large unless you can handle that. 168 169 Note that the data type returned will be a buffer.""" 170 171 cmd = u'SELECT data FROM ref.paperwork_templates WHERE pk = %(pk)s' 172 rows, idx = gmPG2.run_ro_queries (queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}], get_col_idx = False) 173 174 if len(rows) == 0: 175 raise gmExceptions.NoSuchBusinessObjectError('cannot retrieve data for template pk = %s' % self.pk_obj) 176 177 return rows[0][0]
178 179 template_data = property(_get_template_data, lambda x:x) 180 #--------------------------------------------------------
181 - def export_to_file(self, filename=None, chunksize=0):
182 """Export form template from database into file.""" 183 184 if filename is None: 185 if self._payload[self._idx['filename']] is None: 186 suffix = self.__class__._suffix4engine[self._payload[self._idx['engine']]] 187 else: 188 suffix = os.path.splitext(self._payload[self._idx['filename']].strip())[1].strip() 189 if suffix in [u'', u'.']: 190 suffix = self.__class__._suffix4engine[self._payload[self._idx['engine']]] 191 192 filename = gmTools.get_unique_filename ( 193 prefix = 'gm-%s-Template-' % self._payload[self._idx['engine']], 194 suffix = suffix 195 ) 196 197 data_query = { 198 'cmd': u'SELECT substring(data from %(start)s for %(size)s) FROM ref.paperwork_templates WHERE pk = %(pk)s', 199 'args': {'pk': self.pk_obj} 200 } 201 202 data_size_query = { 203 'cmd': u'select octet_length(data) from ref.paperwork_templates where pk = %(pk)s', 204 'args': {'pk': self.pk_obj} 205 } 206 207 result = gmPG2.bytea2file ( 208 data_query = data_query, 209 filename = filename, 210 data_size_query = data_size_query, 211 chunk_size = chunksize 212 ) 213 if result is False: 214 return None 215 216 return filename
217 #--------------------------------------------------------
218 - def update_template_from_file(self, filename=None):
219 gmPG2.file2bytea ( 220 filename = filename, 221 query = u'update ref.paperwork_templates set data = %(data)s::bytea where pk = %(pk)s and xmin = %(xmin)s', 222 args = {'pk': self.pk_obj, 'xmin': self._payload[self._idx['xmin_paperwork_template']]} 223 ) 224 # adjust for xmin change 225 self.refetch_payload()
226 #--------------------------------------------------------
227 - def instantiate(self):
228 fname = self.export_to_file() 229 engine = form_engines[self._payload[self._idx['engine']]] 230 form = engine(template_file = fname) 231 form.template = self 232 return form
233 #============================================================
234 -def get_form_template(name_long=None, external_version=None):
235 cmd = u'select pk from ref.paperwork_templates where name_long = %(lname)s and external_version = %(ver)s' 236 args = {'lname': name_long, 'ver': external_version} 237 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 238 239 if len(rows) == 0: 240 _log.error('cannot load form template [%s - %s]', name_long, external_version) 241 return None 242 243 return cFormTemplate(aPK_obj = rows[0]['pk'])
244 #------------------------------------------------------------
245 -def get_form_templates(engine=None, active_only=False, template_types=None, excluded_types=None):
246 """Load form templates.""" 247 248 args = {'eng': engine, 'in_use': active_only} 249 where_parts = [u'1 = 1'] 250 251 if engine is not None: 252 where_parts.append(u'engine = %(eng)s') 253 254 if active_only: 255 where_parts.append(u'in_use IS true') 256 257 if template_types is not None: 258 args['incl_types'] = tuple(template_types) 259 where_parts.append(u'template_type IN %(incl_types)s') 260 261 if excluded_types is not None: 262 args['excl_types'] = tuple(excluded_types) 263 where_parts.append(u'template_type NOT IN %(excl_types)s') 264 265 cmd = u"SELECT * FROM ref.v_paperwork_templates WHERE %s ORDER BY in_use desc, name_long" % u'\nAND '.join(where_parts) 266 267 rows, idx = gmPG2.run_ro_queries ( 268 queries = [{'cmd': cmd, 'args': args}], 269 get_col_idx = True 270 ) 271 templates = [ cFormTemplate(row = {'pk_field': 'pk_paperwork_template', 'data': r, 'idx': idx}) for r in rows ] 272 273 return templates
274 #------------------------------------------------------------
275 -def create_form_template(template_type=None, name_short=None, name_long=None):
276 277 cmd = u'insert into ref.paperwork_templates (fk_template_type, name_short, name_long, external_version) values (%(type)s, %(nshort)s, %(nlong)s, %(ext_version)s)' 278 rows, idx = gmPG2.run_rw_queries ( 279 queries = [ 280 {'cmd': cmd, 'args': {'type': template_type, 'nshort': name_short, 'nlong': name_long, 'ext_version': 'new'}}, 281 {'cmd': u"select currval(pg_get_serial_sequence('ref.paperwork_templates', 'pk'))"} 282 ], 283 return_data = True 284 ) 285 template = cFormTemplate(aPK_obj = rows[0][0]) 286 return template
287 #------------------------------------------------------------
288 -def delete_form_template(template=None):
289 rows, idx = gmPG2.run_rw_queries ( 290 queries = [ 291 {'cmd': u'delete from ref.paperwork_templates where pk=%(pk)s', 'args': {'pk': template['pk_paperwork_template']}} 292 ] 293 ) 294 return True
295 #============================================================ 296 # OpenOffice/LibreOffice API 297 #============================================================ 298 uno = None 299 cOOoDocumentCloseListener = None 300 writer_binary = None 301 302 #-----------------------------------------------------------
303 -def __configure_path_to_UNO():
304 305 try: 306 which = subprocess.Popen ( 307 args = ('which', 'soffice'), 308 stdout = subprocess.PIPE, 309 stdin = subprocess.PIPE, 310 stderr = subprocess.PIPE, 311 universal_newlines = True 312 ) 313 except (OSError, ValueError, subprocess.CalledProcessError): 314 _log.exception('there was a problem executing [which soffice]') 315 return 316 317 soffice_path, err = which.communicate() 318 soffice_path = soffice_path.strip('\n') 319 uno_path = os.path.abspath ( os.path.join ( 320 os.path.dirname(os.path.realpath(soffice_path)), 321 '..', 322 'basis-link', 323 'program' 324 )) 325 326 _log.info('UNO should be at [%s], appending to sys.path', uno_path) 327 328 sys.path.append(uno_path)
329 #-----------------------------------------------------------
330 -def init_ooo():
331 """FIXME: consider this: 332 333 try: 334 import uno 335 except: 336 print "This Script needs to be run with the python from OpenOffice.org" 337 print "Example: /opt/OpenOffice.org/program/python %s" % ( 338 os.path.basename(sys.argv[0])) 339 print "Or you need to insert the right path at the top, where uno.py is." 340 print "Default: %s" % default_path 341 """ 342 global uno 343 if uno is not None: 344 return 345 346 try: 347 import uno 348 except ImportError: 349 __configure_path_to_UNO() 350 import uno 351 352 global unohelper, oooXCloseListener, oooNoConnectException, oooPropertyValue 353 354 import unohelper 355 from com.sun.star.util import XCloseListener as oooXCloseListener 356 from com.sun.star.connection import NoConnectException as oooNoConnectException 357 from com.sun.star.beans import PropertyValue as oooPropertyValue 358 359 #---------------------------------- 360 class _cOOoDocumentCloseListener(unohelper.Base, oooXCloseListener): 361 """Listens for events sent by OOo during the document closing 362 sequence and notifies the GNUmed client GUI so it can 363 import the closed document into the database. 364 """ 365 def __init__(self, document=None): 366 self.document = document
367 368 def queryClosing(self, evt, owner): 369 # owner is True/False whether I am the owner of the doc 370 pass 371 372 def notifyClosing(self, evt): 373 pass 374 375 def disposing(self, evt): 376 self.document.on_disposed_by_ooo() 377 self.document = None 378 #---------------------------------- 379 380 global cOOoDocumentCloseListener 381 cOOoDocumentCloseListener = _cOOoDocumentCloseListener 382 383 # search for writer binary 384 global writer_binary 385 found, binary = gmShellAPI.find_first_binary(binaries = [ 386 'lowriter', 387 'oowriter' 388 ]) 389 if found: 390 _log.debug('OOo/LO writer binary found: %s', binary) 391 writer_binary = binary 392 else: 393 _log.debug('OOo/LO writer binary NOT found') 394 raise ImportError('LibreOffice/OpenOffice (lowriter/oowriter) not found') 395 396 _log.debug('python UNO bridge successfully initialized') 397 398 #------------------------------------------------------------
399 -class gmOOoConnector(gmBorg.cBorg):
400 """This class handles the connection to OOo. 401 402 Its Singleton instance stays around once initialized. 403 """ 404 # FIXME: need to detect closure of OOo !
405 - def __init__(self):
406 407 init_ooo() 408 409 self.__setup_connection_string() 410 411 self.resolver_uri = "com.sun.star.bridge.UnoUrlResolver" 412 self.desktop_uri = "com.sun.star.frame.Desktop" 413 414 self.max_connect_attempts = 5 415 416 self.local_context = uno.getComponentContext() 417 self.uri_resolver = self.local_context.ServiceManager.createInstanceWithContext(self.resolver_uri, self.local_context) 418 419 self.__desktop = None
420 #-------------------------------------------------------- 421 # external API 422 #--------------------------------------------------------
423 - def cleanup(self, force=True):
424 if self.__desktop is None: 425 _log.debug('no desktop, no cleanup') 426 return 427 428 try: 429 self.__desktop.terminate() 430 except: 431 _log.exception('cannot terminate OOo desktop')
432 #--------------------------------------------------------
433 - def open_document(self, filename=None):
434 """<filename> must be absolute""" 435 if self.desktop is None: 436 _log.error('cannot access OOo desktop') 437 return None 438 439 filename = os.path.expanduser(filename) 440 filename = os.path.abspath(filename) 441 document_uri = uno.systemPathToFileUrl(filename) 442 443 _log.debug('%s -> %s', filename, document_uri) 444 445 doc = self.desktop.loadComponentFromURL(document_uri, "_blank", 0, ()) 446 return doc
447 #-------------------------------------------------------- 448 # internal helpers 449 #--------------------------------------------------------
450 - def __get_startup_settle_time(self):
451 # later factor this out ! 452 dbcfg = gmCfg.cCfgSQL() 453 self.ooo_startup_settle_time = dbcfg.get2 ( 454 option = u'external.ooo.startup_settle_time', 455 workplace = gmSurgery.gmCurrentPractice().active_workplace, 456 bias = u'workplace', 457 default = 3.0 458 )
459 #--------------------------------------------------------
460 - def __setup_connection_string(self):
461 462 # socket: 463 # ooo_port = u'2002' 464 # #self.ooo_start_cmd = 'oowriter -invisible -norestore -nofirststartwizard -nologo -accept="socket,host=localhost,port=%s;urp;StarOffice.ServiceManager"' % ooo_port 465 # self.ooo_start_cmd = 'oowriter -invisible -norestore -accept="socket,host=localhost,port=%s;urp;"' % ooo_port 466 # self.remote_context_uri = "uno:socket,host=localhost,port=%s;urp;StarOffice.ComponentContext" % ooo_port 467 468 # pipe: 469 pipe_name = "uno-gm2lo-%s" % str(random.random())[2:] 470 _log.debug('expecting OOo/LO server on named pipe [%s]', pipe_name) 471 self.ooo_start_cmd = '%s --invisible --norestore --accept="pipe,name=%s;urp" &' % ( 472 writer_binary, 473 pipe_name 474 ) 475 _log.debug('startup command: %s', self.ooo_start_cmd) 476 477 self.remote_context_uri = "uno:pipe,name=%s;urp;StarOffice.ComponentContext" % pipe_name 478 _log.debug('remote context URI: %s', self.remote_context_uri)
479 #--------------------------------------------------------
480 - def __startup_ooo(self):
481 _log.info('trying to start OOo server') 482 _log.debug('startup command: %s', self.ooo_start_cmd) 483 os.system(self.ooo_start_cmd) 484 self.__get_startup_settle_time() 485 _log.debug('waiting %s seconds for OOo to start up', self.ooo_startup_settle_time) 486 time.sleep(self.ooo_startup_settle_time)
487 #-------------------------------------------------------- 488 # properties 489 #--------------------------------------------------------
490 - def _get_desktop(self):
491 if self.__desktop is not None: 492 return self.__desktop 493 494 self.remote_context = None 495 496 attempts = self.max_connect_attempts 497 while attempts > 0: 498 499 _log.debug(u'attempt %s/%s', self.max_connect_attempts - attempts + 1, self.max_connect_attempts) 500 501 try: 502 self.remote_context = self.uri_resolver.resolve(self.remote_context_uri) 503 break 504 except oooNoConnectException: 505 _log.exception('cannot connect to OOo') 506 507 # first loop ? 508 if attempts == self.max_connect_attempts: 509 self.__startup_ooo() 510 else: 511 time.sleep(1) 512 513 attempts = attempts - 1 514 515 if self.remote_context is None: 516 raise OSError(-1, u'cannot connect to OpenOffice', self.remote_context_uri) 517 518 _log.debug('connection seems established') 519 self.__desktop = self.remote_context.ServiceManager.createInstanceWithContext(self.desktop_uri, self.remote_context) 520 _log.debug('got OOo desktop handle') 521 return self.__desktop
522 523 desktop = property(_get_desktop, lambda x:x)
524 #------------------------------------------------------------
525 -class cOOoLetter(object):
526
527 - def __init__(self, template_file=None, instance_type=None):
528 529 self.template_file = template_file 530 self.instance_type = instance_type 531 self.ooo_doc = None
532 #-------------------------------------------------------- 533 # external API 534 #--------------------------------------------------------
535 - def open_in_ooo(self):
536 # connect to OOo 537 ooo_srv = gmOOoConnector() 538 539 # open doc in OOo 540 self.ooo_doc = ooo_srv.open_document(filename = self.template_file) 541 if self.ooo_doc is None: 542 _log.error('cannot open document in OOo') 543 return False 544 545 # listen for close events 546 pat = gmPerson.gmCurrentPatient() 547 pat.locked = True 548 listener = cOOoDocumentCloseListener(document = self) 549 self.ooo_doc.addCloseListener(listener) 550 551 return True
552 #--------------------------------------------------------
553 - def show(self, visible=True):
554 self.ooo_doc.CurrentController.Frame.ContainerWindow.setVisible(visible)
555 #--------------------------------------------------------
556 - def replace_placeholders(self, handler=None, old_style_too = True):
557 558 # new style embedded, implicit placeholders 559 searcher = self.ooo_doc.createSearchDescriptor() 560 searcher.SearchCaseSensitive = False 561 searcher.SearchRegularExpression = True 562 searcher.SearchWords = True 563 searcher.SearchString = handler.placeholder_regex 564 565 placeholder_instance = self.ooo_doc.findFirst(searcher) 566 while placeholder_instance is not None: 567 try: 568 val = handler[placeholder_instance.String] 569 except: 570 val = _('error with placeholder [%s]') % placeholder_instance.String 571 _log.exception(val) 572 573 if val is None: 574 val = _('error with placeholder [%s]') % placeholder_instance.String 575 576 placeholder_instance.String = val 577 placeholder_instance = self.ooo_doc.findNext(placeholder_instance.End, searcher) 578 579 if not old_style_too: 580 return 581 582 # old style "explicit" placeholders 583 text_fields = self.ooo_doc.getTextFields().createEnumeration() 584 while text_fields.hasMoreElements(): 585 text_field = text_fields.nextElement() 586 587 # placeholder ? 588 if not text_field.supportsService('com.sun.star.text.TextField.JumpEdit'): 589 continue 590 # placeholder of type text ? 591 if text_field.PlaceHolderType != 0: 592 continue 593 594 replacement = handler[text_field.PlaceHolder] 595 if replacement is None: 596 continue 597 598 text_field.Anchor.setString(replacement)
599 #--------------------------------------------------------
600 - def save_in_ooo(self, filename=None):
601 if filename is not None: 602 target_url = uno.systemPathToFileUrl(os.path.abspath(os.path.expanduser(filename))) 603 save_args = ( 604 oooPropertyValue('Overwrite', 0, True, 0), 605 oooPropertyValue('FormatFilter', 0, 'swriter: StarOffice XML (Writer)', 0) 606 607 ) 608 # "store AS url" stores the doc, marks it unmodified and updates 609 # the internal media descriptor - as opposed to "store TO url" 610 self.ooo_doc.storeAsURL(target_url, save_args) 611 else: 612 self.ooo_doc.store()
613 #--------------------------------------------------------
614 - def close_in_ooo(self):
615 self.ooo_doc.dispose() 616 pat = gmPerson.gmCurrentPatient() 617 pat.locked = False 618 self.ooo_doc = None
619 #--------------------------------------------------------
620 - def on_disposed_by_ooo(self):
621 # get current file name from OOo, user may have used Save As 622 filename = uno.fileUrlToSystemPath(self.ooo_doc.URL) 623 # tell UI to import the file 624 gmDispatcher.send ( 625 signal = u'import_document_from_file', 626 filename = filename, 627 document_type = self.instance_type, 628 unlock_patient = True 629 ) 630 self.ooo_doc = None
631 #-------------------------------------------------------- 632 # internal helpers 633 #-------------------------------------------------------- 634 635 #============================================================
636 -class cFormEngine(object):
637 """Ancestor for forms.""" 638
639 - def __init__(self, template_file=None):
640 self.template = None 641 self.template_filename = template_file
642 #--------------------------------------------------------
643 - def substitute_placeholders(self, data_source=None):
644 """Parse the template into an instance and replace placeholders with values.""" 645 raise NotImplementedError
646 #--------------------------------------------------------
647 - def edit(self):
648 """Allow editing the instance of the template.""" 649 raise NotImplementedError
650 #--------------------------------------------------------
651 - def generate_output(self, format=None):
652 """Generate output suitable for further processing outside this class, e.g. printing.""" 653 raise NotImplementedError
654 #--------------------------------------------------------
655 - def process(self, data_source=None):
656 """Merge values into the form template. 657 """ 658 pass
659 #--------------------------------------------------------
660 - def cleanup(self):
661 """ 662 A sop to TeX which can't act as a true filter: to delete temporary files 663 """ 664 pass
665 #--------------------------------------------------------
666 - def exe(self, command):
667 """ 668 Executes the provided command. 669 If command cotains %F. it is substituted with the filename 670 Otherwise, the file is fed in on stdin 671 """ 672 pass
673 #--------------------------------------------------------
674 - def store(self, params=None):
675 """Stores the parameters in the backend. 676 677 - link_obj can be a cursor, a connection or a service name 678 - assigning a cursor to link_obj allows the calling code to 679 group the call to store() into an enclosing transaction 680 (for an example see gmReferral.send_referral()...) 681 """ 682 # some forms may not have values ... 683 if params is None: 684 params = {} 685 patient_clinical = self.patient.get_emr() 686 encounter = patient_clinical.active_encounter['pk_encounter'] 687 # FIXME: get_active_episode is no more 688 #episode = patient_clinical.get_active_episode()['pk_episode'] 689 # generate "forever unique" name 690 cmd = "select name_short || ': <' || name_long || '::' || external_version || '>' from paperwork_templates where pk=%s"; 691 rows = gmPG.run_ro_query('reference', cmd, None, self.pk_def) 692 form_name = None 693 if rows is None: 694 _log.error('error retrieving form def for [%s]' % self.pk_def) 695 elif len(rows) == 0: 696 _log.error('no form def for [%s]' % self.pk_def) 697 else: 698 form_name = rows[0][0] 699 # we didn't get a name but want to store the form anyhow 700 if form_name is None: 701 form_name=time.time() # hopefully unique enough 702 # in one transaction 703 queries = [] 704 # - store form instance in form_instance 705 cmd = "insert into form_instances(fk_form_def, form_name, fk_episode, fk_encounter) values (%s, %s, %s, %s)" 706 queries.append((cmd, [self.pk_def, form_name, episode, encounter])) 707 # - store params in form_data 708 for key in params.keys(): 709 cmd = """ 710 insert into form_data(fk_instance, place_holder, value) 711 values ((select currval('form_instances_pk_seq')), %s, %s::text) 712 """ 713 queries.append((cmd, [key, params[key]])) 714 # - get inserted PK 715 queries.append(("select currval ('form_instances_pk_seq')", [])) 716 status, err = gmPG.run_commit('historica', queries, True) 717 if status is None: 718 _log.error('failed to store form [%s] (%s): %s' % (self.pk_def, form_name, err)) 719 return None 720 return status
721 722 #================================================================ 723 # OOo template forms 724 #----------------------------------------------------------------
725 -class cOOoForm(cFormEngine):
726 """A forms engine wrapping OOo.""" 727
728 - def __init__(self, template_file=None):
729 super(self.__class__, self).__init__(template_file = template_file) 730 731 path, ext = os.path.splitext(self.template_filename) 732 if ext in [r'', r'.']: 733 ext = r'.odt' 734 self.instance_filename = r'%s-instance%s' % (path, ext)
735 736 #================================================================ 737 # AbiWord template forms 738 #----------------------------------------------------------------
739 -class cAbiWordForm(cFormEngine):
740 """A forms engine wrapping AbiWord.""" 741 742 placeholder_regex = r'\$&lt;.+?&gt;\$' 743
744 - def __init__(self, template_file=None):
745 746 super(cAbiWordForm, self).__init__(template_file = template_file) 747 748 # detect abiword 749 found, self.abiword_binary = gmShellAPI.detect_external_binary(binary = r'abiword') 750 if not found: 751 raise ImportError('<abiword(.exe)> not found')
752 #--------------------------------------------------------
753 - def substitute_placeholders(self, data_source=None):
754 # should *actually* properly parse the XML 755 756 path, ext = os.path.splitext(self.template_filename) 757 if ext in [r'', r'.']: 758 ext = r'.abw' 759 self.instance_filename = r'%s-instance%s' % (path, ext) 760 761 template_file = codecs.open(self.template_filename, 'rU', 'utf8') 762 instance_file = codecs.open(self.instance_filename, 'wb', 'utf8') 763 764 if self.template is not None: 765 # inject placeholder values 766 data_source.set_placeholder(u'form_name_long', self.template['name_long']) 767 data_source.set_placeholder(u'form_name_short', self.template['name_short']) 768 data_source.set_placeholder(u'form_version', self.template['external_version']) 769 770 for line in template_file: 771 772 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 773 instance_file.write(line) 774 continue 775 776 # 1) find placeholders in this line 777 placeholders_in_line = regex.findall(cAbiWordForm.placeholder_regex, line, regex.IGNORECASE) 778 # 2) and replace them 779 for placeholder in placeholders_in_line: 780 try: 781 val = data_source[placeholder.replace(u'&lt;', u'<').replace(u'&gt;', u'>')] 782 except: 783 val = _('error with placeholder [%s]') % gmTools.xml_escape_string(placeholder) 784 _log.exception(val) 785 786 if val is None: 787 val = _('error with placeholder [%s]') % gmTools.xml_escape_string(placeholder) 788 789 line = line.replace(placeholder, val) 790 791 instance_file.write(line) 792 793 instance_file.close() 794 template_file.close() 795 796 if self.template is not None: 797 # remove temporary placeholders 798 data_source.unset_placeholder(u'form_name_long') 799 data_source.unset_placeholder(u'form_name_short') 800 data_source.unset_placeholder(u'form_version') 801 802 return
803 #--------------------------------------------------------
804 - def edit(self):
805 enc = sys.getfilesystemencoding() 806 cmd = (r'%s %s' % (self.abiword_binary, self.instance_filename.encode(enc))).encode(enc) 807 result = gmShellAPI.run_command_in_shell(command = cmd, blocking = True) 808 self.re_editable_filenames = [] 809 return result
810 #--------------------------------------------------------
811 - def generate_output(self, instance_file=None, format=None):
812 self.final_output_filenames = [self.instance_filename] 813 return self.instance_filename
814 #---------------------------------------------------------------- 815 form_engines[u'A'] = cAbiWordForm 816 817 #================================================================ 818 # LaTeX template forms 819 #----------------------------------------------------------------
820 -class cLaTeXForm(cFormEngine):
821 """A forms engine wrapping LaTeX.""" 822
823 - def __init__(self, template_file=None):
824 super(self.__class__, self).__init__(template_file = template_file) 825 path, ext = os.path.splitext(self.template_filename) 826 if ext in [r'', r'.']: 827 ext = r'.tex' 828 self.instance_filename = r'%s-instance%s' % (path, ext)
829 #--------------------------------------------------------
830 - def substitute_placeholders(self, data_source=None):
831 832 template_file = codecs.open(self.template_filename, 'rU', 'utf8') 833 instance_file = codecs.open(self.instance_filename, 'wb', 'utf8') 834 835 if self.template is not None: 836 # inject placeholder values 837 data_source.set_placeholder(u'form_name_long', self.template['name_long']) 838 data_source.set_placeholder(u'form_name_short', self.template['name_short']) 839 data_source.set_placeholder(u'form_version', self.template['external_version']) 840 841 for line in template_file: 842 843 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 844 instance_file.write(line) 845 continue 846 847 # 1) find placeholders in this line 848 placeholders_in_line = regex.findall(data_source.placeholder_regex, line, regex.IGNORECASE) 849 # 2) and replace them 850 for placeholder in placeholders_in_line: 851 try: 852 val = data_source[placeholder] 853 except: 854 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder) 855 _log.exception(val) 856 857 if val is None: 858 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder) 859 860 line = line.replace(placeholder, val) 861 862 instance_file.write(line) 863 864 instance_file.close() 865 self.re_editable_filenames = [self.instance_filename] 866 template_file.close() 867 868 if self.template is not None: 869 # remove temporary placeholders 870 data_source.unset_placeholder(u'form_name_long') 871 data_source.unset_placeholder(u'form_name_short') 872 data_source.unset_placeholder(u'form_version') 873 874 return
875 #--------------------------------------------------------
876 - def edit(self):
877 878 mimetypes = [ 879 u'application/x-latex', 880 u'application/x-tex', 881 u'text/plain' 882 ] 883 884 for mimetype in mimetypes: 885 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename) 886 if editor_cmd is not None: 887 break 888 889 if editor_cmd is None: 890 # LaTeX code is text: also consider text *viewers* 891 # since pretty much any of them will be an editor as well 892 for mimetype in mimetypes: 893 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename) 894 if editor_cmd is not None: 895 break 896 897 # last resort 898 if editor_cmd is None: 899 if os.name == 'nt': 900 editor_cmd = u'notepad.exe %s' % self.instance_filename 901 else: 902 editor_cmd = u'sensible-editor %s' % self.instance_filename 903 904 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 905 self.re_editable_filenames = [self.instance_filename] 906 907 return result
908 #--------------------------------------------------------
909 - def generate_output(self, instance_file=None, format=None):
910 911 if instance_file is None: 912 instance_file = self.instance_filename 913 914 try: 915 open(instance_file, 'r').close() 916 except: 917 _log.exception('cannot access form instance file [%s]', instance_file) 918 gmLog2.log_stack_trace() 919 return None 920 921 self.instance_filename = instance_file 922 923 _log.debug('ignoring <format> directive [%s], generating PDF', format) 924 925 # create sandbox for LaTeX to play in 926 sandbox_dir = os.path.splitext(self.template_filename)[0] 927 _log.debug('LaTeX sandbox directory: [%s]', sandbox_dir) 928 929 gmTools.mkdir(sandbox_dir) 930 931 sandboxed_instance_filename = os.path.join(sandbox_dir, os.path.split(self.instance_filename)[1]) 932 shutil.move(self.instance_filename, sandboxed_instance_filename) 933 self.re_editable_filenames = [sandboxed_instance_filename] 934 935 # LaTeX can need up to three runs to get cross references et al right 936 if platform.system() == 'Windows': 937 draft_cmd = r'pdflatex.exe -draftmode -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 938 final_cmd = r'pdflatex.exe -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 939 else: 940 draft_cmd = r'pdflatex -draftmode -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 941 final_cmd = r'pdflatex -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 942 for run_cmd in [draft_cmd, draft_cmd, final_cmd]: 943 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]): 944 _log.error('problem running pdflatex, cannot generate form output') 945 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdflatex. Cannot turn LaTeX template into PDF.'), beep = True) 946 return None 947 948 sandboxed_pdf_name = u'%s.pdf' % os.path.splitext(sandboxed_instance_filename)[0] 949 target_dir = os.path.split(self.instance_filename)[0] 950 try: 951 shutil.move(sandboxed_pdf_name, target_dir) 952 except IOError: 953 _log.exception('cannot move sandboxed PDF: %s -> %s', sandboxed_pdf_name, target_dir) 954 gmDispatcher.send(signal = 'statustext', msg = _('Sandboxed PDF output file cannot be moved.'), beep = True) 955 return None 956 957 final_pdf_name = u'%s.pdf' % os.path.splitext(self.instance_filename)[0] 958 959 try: 960 open(final_pdf_name, 'r').close() 961 except IOError: 962 _log.exception('cannot open target PDF: %s', final_pdf_name) 963 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True) 964 return None 965 966 self.final_output_filenames = [final_pdf_name] 967 968 return final_pdf_name
969 #------------------------------------------------------------ 970 form_engines[u'L'] = cLaTeXForm 971 972 #================================================================ 973 # Xe(La)TeX template forms 974 #---------------------------------------------------------------- 975 # Xe(La)TeX: http://www.scholarsfonts.net/xetextt.pdf
976 -class cXeTeXForm(cFormEngine):
977 """A forms engine wrapping Xe(La)TeX.""" 978
979 - def __init__(self, template_file=None):
980 super(self.__class__, self).__init__(template_file = template_file) 981 path, ext = os.path.splitext(self.template_filename) 982 if ext in [r'', r'.']: 983 ext = r'.tex' 984 self.instance_filename = r'%s-instance%s' % (path, ext)
985 #--------------------------------------------------------
986 - def substitute_placeholders(self, data_source=None):
987 988 template_file = codecs.open(self.template_filename, 'rU', 'utf8') 989 instance_file = codecs.open(self.instance_filename, 'wb', 'utf8') 990 991 if self.template is not None: 992 # inject placeholder values 993 data_source.set_placeholder(u'form_name_long', self.template['name_long']) 994 data_source.set_placeholder(u'form_name_short', self.template['name_short']) 995 data_source.set_placeholder(u'form_version', self.template['external_version']) 996 997 for line in template_file: 998 999 if line.strip() in [u'', u'\r', u'\n', u'\r\n']: 1000 instance_file.write(line) 1001 continue 1002 1003 # 1) find placeholders in this line 1004 placeholders_in_line = regex.findall(data_source.placeholder_regex, line, regex.IGNORECASE) 1005 # 2) and replace them 1006 for placeholder in placeholders_in_line: 1007 try: 1008 val = data_source[placeholder] 1009 except: 1010 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder, replace_known_unicode=False) 1011 _log.exception(val) 1012 1013 if val is None: 1014 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder, replace_known_unicode=False) 1015 1016 line = line.replace(placeholder, val) 1017 1018 instance_file.write(line) 1019 1020 instance_file.close() 1021 self.re_editable_filenames = [self.instance_filename] 1022 template_file.close() 1023 1024 if self.template is not None: 1025 # remove temporary placeholders 1026 data_source.unset_placeholder(u'form_name_long') 1027 data_source.unset_placeholder(u'form_name_short') 1028 data_source.unset_placeholder(u'form_version') 1029 1030 return
1031 #--------------------------------------------------------
1032 - def edit(self):
1033 1034 mimetypes = [ 1035 u'application/x-xetex', 1036 u'application/x-latex', 1037 u'application/x-tex', 1038 u'text/plain' 1039 ] 1040 1041 for mimetype in mimetypes: 1042 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename) 1043 if editor_cmd is not None: 1044 break 1045 1046 if editor_cmd is None: 1047 # Xe(La)TeX code is utf8: also consider text *viewers* 1048 # since pretty much any of them will be an editor as well 1049 for mimetype in mimetypes: 1050 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.instance_filename) 1051 if editor_cmd is not None: 1052 break 1053 1054 # last resort 1055 if editor_cmd is None: 1056 if os.name == 'nt': 1057 editor_cmd = u'notepad.exe %s' % self.instance_filename 1058 else: 1059 editor_cmd = u'sensible-editor %s' % self.instance_filename 1060 1061 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 1062 self.re_editable_filenames = [self.instance_filename] 1063 1064 return result
1065 #--------------------------------------------------------
1066 - def generate_output(self, instance_file=None, format=None):
1067 1068 if instance_file is None: 1069 instance_file = self.instance_filename 1070 1071 try: 1072 open(instance_file, 'r').close() 1073 except: 1074 _log.exception('cannot access form instance file [%s]', instance_file) 1075 gmLog2.log_stack_trace() 1076 return None 1077 1078 self.instance_filename = instance_file 1079 1080 _log.debug('ignoring <format> directive [%s], generating PDF', format) 1081 1082 # create sandbox for Xe(La)TeX to play in 1083 sandbox_dir = os.path.splitext(self.template_filename)[0] 1084 _log.debug('Xe(La)TeX sandbox directory: [%s]', sandbox_dir) 1085 1086 gmTools.mkdir(sandbox_dir) 1087 1088 sandboxed_instance_filename = os.path.join(sandbox_dir, os.path.split(self.instance_filename)[1]) 1089 shutil.move(self.instance_filename, sandboxed_instance_filename) 1090 self.re_editable_filenames = [sandboxed_instance_filename] 1091 1092 # Xe(La)TeX can need up to three runs to get cross references et al right 1093 if platform.system() == 'Windows': 1094 # not yet supported: -draftmode 1095 # does not support: -shell-escape 1096 draft_cmd = r'xelatex.exe -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 1097 final_cmd = r'xelatex.exe -interaction=nonstopmode -output-directory=%s %s' % (sandbox_dir, sandboxed_instance_filename) 1098 else: 1099 # not yet supported: -draftmode 1100 draft_cmd = r'xelatex -interaction=nonstopmode -output-directory=%s -shell-escape %s' % (sandbox_dir, sandboxed_instance_filename) 1101 final_cmd = r'xelatex -interaction=nonstopmode -output-directory=%s -shell-escape %s' % (sandbox_dir, sandboxed_instance_filename) 1102 for run_cmd in [draft_cmd, draft_cmd, final_cmd]: 1103 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]): 1104 _log.error('problem running xelatex, cannot generate form output') 1105 gmDispatcher.send(signal = 'statustext', msg = _('Error running xelatex. Cannot turn Xe(La)TeX template into PDF.'), beep = True) 1106 return None 1107 1108 sandboxed_pdf_name = u'%s.pdf' % os.path.splitext(sandboxed_instance_filename)[0] 1109 target_dir = os.path.split(self.instance_filename)[0] 1110 try: 1111 shutil.move(sandboxed_pdf_name, target_dir) 1112 except IOError: 1113 _log.exception('cannot move sandboxed PDF: %s -> %s', sandboxed_pdf_name, target_dir) 1114 gmDispatcher.send(signal = 'statustext', msg = _('Sandboxed PDF output file cannot be moved.'), beep = True) 1115 return None 1116 1117 final_pdf_name = u'%s.pdf' % os.path.splitext(self.instance_filename)[0] 1118 1119 try: 1120 open(final_pdf_name, 'r').close() 1121 except IOError: 1122 _log.exception('cannot open target PDF: %s', final_pdf_name) 1123 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True) 1124 return None 1125 1126 self.final_output_filenames = [final_pdf_name] 1127 1128 return final_pdf_name
1129 #------------------------------------------------------------ 1130 form_engines[u'X'] = cXeTeXForm 1131 1132 #============================================================ 1133 # Gnuplot template forms 1134 #------------------------------------------------------------
1135 -class cGnuplotForm(cFormEngine):
1136 """A forms engine wrapping Gnuplot.""" 1137 1138 #--------------------------------------------------------
1139 - def substitute_placeholders(self, data_source=None):
1140 """Parse the template into an instance and replace placeholders with values.""" 1141 pass
1142 #--------------------------------------------------------
1143 - def edit(self):
1144 """Allow editing the instance of the template.""" 1145 self.re_editable_filenames = [] 1146 return True
1147 #--------------------------------------------------------
1148 - def generate_output(self, format=None):
1149 """Generate output suitable for further processing outside this class, e.g. printing. 1150 1151 Expects .data_filename to be set. 1152 """ 1153 self.conf_filename = gmTools.get_unique_filename(prefix = 'gm2gpl-', suffix = '.conf') 1154 conf_file = codecs.open(self.conf_filename, 'wb', 'utf8') 1155 conf_file.write('# setting the gnuplot data file\n') 1156 conf_file.write("gm2gpl_datafile = '%s'\n" % self.data_filename) 1157 conf_file.close() 1158 1159 # FIXME: cater for configurable path 1160 if platform.system() == 'Windows': 1161 exec_name = 'gnuplot.exe' 1162 else: 1163 exec_name = 'gnuplot' 1164 1165 args = [exec_name, '-p', self.conf_filename, self.template_filename] 1166 _log.debug('plotting args: %s' % str(args)) 1167 1168 try: 1169 gp = subprocess.Popen ( 1170 args = args, 1171 close_fds = True 1172 ) 1173 except (OSError, ValueError, subprocess.CalledProcessError): 1174 _log.exception('there was a problem executing gnuplot') 1175 gmDispatcher.send(signal = u'statustext', msg = _('Error running gnuplot. Cannot plot data.'), beep = True) 1176 return 1177 1178 gp.communicate() 1179 1180 self.final_output_filenames = [ 1181 self.conf_filename, 1182 self.data_filename, 1183 self.template_filename 1184 ] 1185 1186 return
1187 #------------------------------------------------------------ 1188 form_engines[u'G'] = cGnuplotForm 1189 1190 #============================================================ 1191 # fPDF form engine 1192 #------------------------------------------------------------
1193 -class cPDFForm(cFormEngine):
1194 """A forms engine wrapping PDF forms. 1195 1196 Johann Felix Soden <johfel@gmx.de> helped with this. 1197 1198 http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf 1199 1200 http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/fdf_data_exchange.pdf 1201 """ 1202
1203 - def __init__(self, template_file=None):
1204 1205 super(cPDFForm, self).__init__(template_file = template_file) 1206 1207 # detect pdftk 1208 found, self.pdftk_binary = gmShellAPI.detect_external_binary(binary = r'pdftk') 1209 if not found: 1210 raise ImportError('<pdftk(.exe)> not found') 1211 return # should be superfluous, actually 1212 1213 enc = sys.getfilesystemencoding() 1214 self.pdftk_binary = self.pdftk_binary.encode(enc) 1215 1216 base_name, ext = os.path.splitext(self.template_filename) 1217 self.fdf_dumped_filename = (u'%s.fdf' % base_name).encode(enc) 1218 self.fdf_replaced_filename = (u'%s-replaced.fdf' % base_name).encode(enc) 1219 self.pdf_filled_filename = (u'%s-filled.pdf' % base_name).encode(enc) 1220 self.pdf_flattened_filename = (u'%s-filled-flattened.pdf' % base_name).encode(enc)
1221 #--------------------------------------------------------
1222 - def substitute_placeholders(self, data_source=None):
1223 1224 # dump form fields from template 1225 cmd_line = [ 1226 self.pdftk_binary, 1227 self.template_filename, 1228 r'generate_fdf', 1229 r'output', 1230 self.fdf_dumped_filename 1231 ] 1232 _log.debug(u' '.join(cmd_line)) 1233 try: 1234 pdftk = subprocess.Popen(cmd_line) 1235 except OSError: 1236 _log.exception('cannot run <pdftk> (dump data from form)') 1237 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot extract fields from PDF form template.'), beep = True) 1238 return False 1239 1240 pdftk.communicate() 1241 if pdftk.returncode != 0: 1242 _log.error('<pdftk> returned [%s], failed to dump data from PDF form into FDF', pdftk.returncode) 1243 return False 1244 1245 # parse dumped FDF file for "/V (...)" records 1246 # and replace placeholders therein 1247 fdf_dumped_file = open(self.fdf_dumped_filename, 'rbU') 1248 fdf_replaced_file = codecs.open(self.fdf_replaced_filename, 'wb') 1249 1250 string_value_regex = r'\s*/V\s*\(.+\)\s*$' 1251 for line in fdf_dumped_file: 1252 if not regex.match(string_value_regex, line): 1253 fdf_replaced_file.write(line) 1254 continue 1255 1256 # strip cruft around the string value 1257 raw_str_val = line.strip() # remove framing whitespace 1258 raw_str_val = raw_str_val[2:] # remove leading "/V" 1259 raw_str_val = raw_str_val.lstrip() # remove whitespace between "/V" and "(" 1260 raw_str_val = raw_str_val[1:] # remove opening "(" 1261 raw_str_val = raw_str_val[2:] # remove BOM-16-BE 1262 raw_str_val = raw_str_val.rstrip() # remove trailing whitespace 1263 raw_str_val = raw_str_val[:-1] # remove closing ")" 1264 1265 # work on FDF escapes 1266 raw_str_val = raw_str_val.replace('\(', '(') # remove escaping of "(" 1267 raw_str_val = raw_str_val.replace('\)', ')') # remove escaping of ")" 1268 1269 # by now raw_str_val should contain the actual 1270 # string value, albeit encoded as UTF-16, so 1271 # decode it into a unicode object, 1272 # split multi-line fields on "\n" literal 1273 raw_str_lines = raw_str_val.split('\x00\\n') 1274 value_template_lines = [] 1275 for raw_str_line in raw_str_lines: 1276 value_template_lines.append(raw_str_line.decode('utf_16_be')) 1277 1278 replaced_lines = [] 1279 for value_template in value_template_lines: 1280 # find any placeholders within 1281 placeholders_in_value = regex.findall(data_source.placeholder_regex, value_template, regex.IGNORECASE) 1282 for placeholder in placeholders_in_value: 1283 try: 1284 replacement = data_source[placeholder] 1285 except: 1286 _log.exception(replacement) 1287 replacement = _('error with placeholder [%s]') % placeholder 1288 if replacement is None: 1289 replacement = _('error with placeholder [%s]') % placeholder 1290 value_template = value_template.replace(placeholder, replacement) 1291 1292 value_template = value_template.encode('utf_16_be') 1293 1294 if len(placeholders_in_value) > 0: 1295 value_template = value_template.replace(r'(', r'\(') 1296 value_template = value_template.replace(r')', r'\)') 1297 1298 replaced_lines.append(value_template) 1299 1300 replaced_line = '\x00\\n'.join(replaced_lines) 1301 1302 fdf_replaced_file.write('/V (') 1303 fdf_replaced_file.write(codecs.BOM_UTF16_BE) 1304 fdf_replaced_file.write(replaced_line) 1305 fdf_replaced_file.write(')\n') 1306 1307 fdf_replaced_file.close() 1308 fdf_dumped_file.close() 1309 1310 # merge replaced data back into form 1311 cmd_line = [ 1312 self.pdftk_binary, 1313 self.template_filename, 1314 r'fill_form', 1315 self.fdf_replaced_filename, 1316 r'output', 1317 self.pdf_filled_filename 1318 ] 1319 _log.debug(u' '.join(cmd_line)) 1320 try: 1321 pdftk = subprocess.Popen(cmd_line) 1322 except OSError: 1323 _log.exception('cannot run <pdftk> (merge data into form)') 1324 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot fill in PDF form template.'), beep = True) 1325 return False 1326 1327 pdftk.communicate() 1328 if pdftk.returncode != 0: 1329 _log.error('<pdftk> returned [%s], failed to merge FDF data into PDF form', pdftk.returncode) 1330 return False 1331 1332 return True
1333 #--------------------------------------------------------
1334 - def edit(self):
1335 mimetypes = [ 1336 u'application/pdf', 1337 u'application/x-pdf' 1338 ] 1339 1340 for mimetype in mimetypes: 1341 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.pdf_filled_filename) 1342 if editor_cmd is not None: 1343 break 1344 1345 if editor_cmd is None: 1346 _log.debug('editor cmd not found, trying viewer cmd') 1347 for mimetype in mimetypes: 1348 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.pdf_filled_filename) 1349 if editor_cmd is not None: 1350 break 1351 1352 if editor_cmd is None: 1353 return False 1354 1355 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 1356 1357 path, fname = os.path.split(self.pdf_filled_filename) 1358 candidate = os.path.join(gmTools.gmPaths().home_dir, fname) 1359 1360 if os.access(candidate, os.R_OK): 1361 _log.debug('filled-in PDF found: %s', candidate) 1362 os.rename(self.pdf_filled_filename, self.pdf_filled_filename + '.bak') 1363 shutil.move(candidate, path) 1364 else: 1365 _log.debug('filled-in PDF not found: %s', candidate) 1366 1367 self.re_editable_filenames = [self.pdf_filled_filename] 1368 1369 return result
1370 #--------------------------------------------------------
1371 - def generate_output(self, format=None):
1372 """Generate output suitable for further processing outside this class, e.g. printing.""" 1373 1374 # eventually flatten the filled in form so we 1375 # can keep both a flattened and an editable copy: 1376 cmd_line = [ 1377 self.pdftk_binary, 1378 self.pdf_filled_filename, 1379 r'output', 1380 self.pdf_flattened_filename, 1381 r'flatten' 1382 ] 1383 _log.debug(u' '.join(cmd_line)) 1384 try: 1385 pdftk = subprocess.Popen(cmd_line) 1386 except OSError: 1387 _log.exception('cannot run <pdftk> (flatten filled in form)') 1388 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot flatten filled in PDF form.'), beep = True) 1389 return None 1390 1391 pdftk.communicate() 1392 if pdftk.returncode != 0: 1393 _log.error('<pdftk> returned [%s], failed to flatten filled in PDF form', pdftk.returncode) 1394 return None 1395 1396 self.final_output_filenames = [self.pdf_flattened_filename] 1397 1398 return self.pdf_flattened_filename
1399 #------------------------------------------------------------ 1400 form_engines[u'P'] = cPDFForm 1401 1402 #============================================================ 1403 # older code 1404 #------------------------------------------------------------
1405 -class cIanLaTeXForm(cFormEngine):
1406 """A forms engine wrapping LaTeX. 1407 """
1408 - def __init__(self, id, template):
1409 self.id = id 1410 self.template = template
1411
1412 - def process (self,params={}):
1413 try: 1414 latex = Cheetah.Template.Template (self.template, filter=LaTeXFilter, searchList=[params]) 1415 # create a 'sandbox' directory for LaTeX to play in 1416 self.tmp = tempfile.mktemp () 1417 os.makedirs (self.tmp) 1418 self.oldcwd = os.getcwd () 1419 os.chdir (self.tmp) 1420 stdin = os.popen ("latex", "w", 2048) 1421 stdin.write (str (latex)) #send text. LaTeX spits it's output into stdout 1422 # FIXME: send LaTeX output to the logger 1423 stdin.close () 1424 if not gmShellAPI.run_command_in_shell("dvips texput.dvi -o texput.ps", blocking=True): 1425 raise FormError ('DVIPS returned error') 1426 except EnvironmentError, e: 1427 _log.error(e.strerror) 1428 raise FormError (e.strerror) 1429 return file ("texput.ps")
1430
1431 - def xdvi (self):
1432 """ 1433 For testing purposes, runs Xdvi on the intermediate TeX output 1434 WARNING: don't try this on Windows 1435 """ 1436 gmShellAPI.run_command_in_shell("xdvi texput.dvi", blocking=True)
1437
1438 - def exe (self, command):
1439 if "%F" in command: 1440 command.replace ("%F", "texput.ps") 1441 else: 1442 command = "%s < texput.ps" % command 1443 try: 1444 if not gmShellAPI.run_command_in_shell(command, blocking=True): 1445 _log.error("external command %s returned non-zero" % command) 1446 raise FormError ('external command %s returned error' % command) 1447 except EnvironmentError, e: 1448 _log.error(e.strerror) 1449 raise FormError (e.strerror) 1450 return True
1451
1452 - def printout (self):
1453 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print') 1454 self.exe (command)
1455
1456 - def cleanup (self):
1457 """ 1458 Delete all the LaTeX output iles 1459 """ 1460 for i in os.listdir ('.'): 1461 os.unlink (i) 1462 os.chdir (self.oldcwd) 1463 os.rmdir (self.tmp)
1464 1465 1466 1467 1468 #================================================================ 1469 # define a class for HTML forms (for printing) 1470 #================================================================
1471 -class cXSLTFormEngine(cFormEngine):
1472 """This class can create XML document from requested data, 1473 then process it with XSLT template and display results 1474 """ 1475 1476 # FIXME: make the path configurable ? 1477 _preview_program = u'oowriter ' #this program must be in the system PATH 1478
1479 - def __init__(self, template=None):
1480 1481 if template is None: 1482 raise ValueError(u'%s: cannot create form instance without a template' % __name__) 1483 1484 cFormEngine.__init__(self, template = template) 1485 1486 self._FormData = None 1487 1488 # here we know/can assume that the template was stored as a utf-8 1489 # encoded string so use that conversion to create unicode: 1490 #self._XSLTData = unicode(str(template.template_data), 'UTF-8') 1491 # but in fact, unicode() knows how to handle buffers, so simply: 1492 self._XSLTData = unicode(self.template.template_data, 'UTF-8', 'strict') 1493 1494 # we must still devise a method of extracting the SQL query: 1495 # - either by retrieving it from a particular tag in the XSLT or 1496 # - by making the stored template actually be a dict which, unpickled, 1497 # has the keys "xslt" and "sql" 1498 self._SQL_query = u'select 1' #this sql query must output valid xml
1499 #-------------------------------------------------------- 1500 # external API 1501 #--------------------------------------------------------
1502 - def process(self, sql_parameters):
1503 """get data from backend and process it with XSLT template to produce readable output""" 1504 1505 # extract SQL (this is wrong but displays what is intended) 1506 xslt = libxml2.parseDoc(self._XSLTData) 1507 root = xslt.children 1508 for child in root: 1509 if child.type == 'element': 1510 self._SQL_query = child.content 1511 break 1512 1513 # retrieve data from backend 1514 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': self._SQL_query, 'args': sql_parameters}], get_col_idx = False) 1515 1516 __header = '<?xml version="1.0" encoding="UTF-8"?>\n' 1517 __body = rows[0][0] 1518 1519 # process XML data according to supplied XSLT, producing HTML 1520 self._XMLData =__header + __body 1521 style = libxslt.parseStylesheetDoc(xslt) 1522 xml = libxml2.parseDoc(self._XMLData) 1523 html = style.applyStylesheet(xml, None) 1524 self._FormData = html.serialize() 1525 1526 style.freeStylesheet() 1527 xml.freeDoc() 1528 html.freeDoc()
1529 #--------------------------------------------------------
1530 - def preview(self):
1531 if self._FormData is None: 1532 raise ValueError, u'Preview request for empty form. Make sure the form is properly initialized and process() was performed' 1533 1534 fname = gmTools.get_unique_filename(prefix = u'gm_XSLT_form-', suffix = u'.html') 1535 #html_file = os.open(fname, 'wb') 1536 #html_file.write(self._FormData.encode('UTF-8')) 1537 html_file = codecs.open(fname, 'wb', 'utf8', 'strict') # or 'replace' ? 1538 html_file.write(self._FormData) 1539 html_file.close() 1540 1541 cmd = u'%s %s' % (self.__class__._preview_program, fname) 1542 1543 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = False): 1544 _log.error('%s: cannot launch report preview program' % __name__) 1545 return False 1546 1547 #os.unlink(self.filename) #delete file 1548 #FIXME: under Windows the temp file is deleted before preview program gets it (under Linux it works OK) 1549 1550 return True
1551 #--------------------------------------------------------
1552 - def print_directly(self):
1553 #not so fast, look at it first 1554 self.preview()
1555 1556 1557 #===================================================== 1558 #class LaTeXFilter(Cheetah.Filters.Filter):
1559 -class LaTeXFilter:
1560 - def filter (self, item, table_sep= " \\\\\n", **kwds):
1561 """ 1562 Convience function to escape ISO-Latin-1 strings for TeX output 1563 WARNING: not all ISO-Latin-1 characters are expressible in TeX 1564 FIXME: nevertheless, there are a few more we could support 1565 1566 Also intelligently convert lists and tuples into TeX-style table lines 1567 """ 1568 if type (item) is types.UnicodeType or type (item) is types.StringType: 1569 item = item.replace ("\\", "\\backslash") # I wonder about this, do we want users to be able to use raw TeX? 1570 item = item.replace ("&", "\\&") 1571 item = item.replace ("$", "\\$") 1572 item = item.replace ('"', "") # okay, that's not right, but easiest solution for now 1573 item = item.replace ("\n", "\\\\ ") 1574 if len (item.strip ()) == 0: 1575 item = "\\relax " # sometimes TeX really hates empty strings, this seems to mollify it 1576 # FIXME: cover all of ISO-Latin-1 which can be expressed in TeX 1577 if type (item) is types.UnicodeType: 1578 item = item.encode ('latin-1', 'replace') 1579 trans = {'ß':'\\ss{}', 'ä': '\\"{a}', 'Ä' :'\\"{A}', 'ö': '\\"{o}', 'Ö': '\\"{O}', 'ü': '\\"{u}', 'Ü': '\\"{U}', 1580 '\x8a':'\\v{S}', '\x8a':'\\OE{}', '\x9a':'\\v{s}', '\x9c': '\\oe{}', '\a9f':'\\"{Y}', #Microsloth extensions 1581 '\x86': '{\\dag}', '\x87': '{\\ddag}', '\xa7':'{\\S}', '\xb6': '{\\P}', '\xa9': '{\\copyright}', '\xbf': '?`', 1582 '\xc0':'\\`{A}', '\xa1': "\\'{A}", '\xa2': '\\^{A}', '\xa3':'\\~{A}', '\\xc5': '{\AA}', 1583 '\xc7':'\\c{C}', '\xc8':'\\`{E}', 1584 '\xa1': '!`', 1585 '\xb5':'$\mu$', '\xa3': '\pounds{}', '\xa2':'cent'} 1586 for k, i in trans.items (): 1587 item = item.replace (k, i) 1588 elif type (item) is types.ListType or type (item) is types.TupleType: 1589 item = string.join ([self.filter (i, ' & ') for i in item], table_sep) 1590 elif item is None: 1591 item = '\\relax % Python None\n' 1592 elif type (item) is types.IntType or type (item) is types.FloatType: 1593 item = str (item) 1594 else: 1595 item = str (item) 1596 _log.warning("unknown type %s, string %s" % (type (item), item)) 1597 return item
1598 1599 1600 #===========================================================
1601 -class cHL7Form (cFormEngine):
1602 pass
1603 1604 #============================================================ 1605 # convenience functions 1606 #------------------------------------------------------------
1607 -def get_form(id):
1608 """ 1609 Instantiates a FormEngine based on the form ID or name from the backend 1610 """ 1611 try: 1612 # it's a number: match to form ID 1613 id = int (id) 1614 cmd = 'select template, engine, pk from paperwork_templates where pk = %s' 1615 except ValueError: 1616 # it's a string, match to the form's name 1617 # FIXME: can we somehow OR like this: where name_short=%s OR name_long=%s ? 1618 cmd = 'select template, engine, flags, pk from paperwork_templates where name_short = %s' 1619 result = gmPG.run_ro_query ('reference', cmd, None, id) 1620 if result is None: 1621 _log.error('error getting form [%s]' % id) 1622 raise gmExceptions.FormError ('error getting form [%s]' % id) 1623 if len(result) == 0: 1624 _log.error('no form [%s] found' % id) 1625 raise gmExceptions.FormError ('no such form found [%s]' % id) 1626 if result[0][1] == 'L': 1627 return LaTeXForm (result[0][2], result[0][0]) 1628 elif result[0][1] == 'T': 1629 return TextForm (result[0][2], result[0][0]) 1630 else: 1631 _log.error('no form engine [%s] for form [%s]' % (result[0][1], id)) 1632 raise FormError ('no engine [%s] for form [%s]' % (result[0][1], id))
1633 #-------------------------------------------------------------
1634 -class FormError (Exception):
1635 - def __init__ (self, value):
1636 self.value = value
1637
1638 - def __str__ (self):
1639 return repr (self.value)
1640 #------------------------------------------------------------- 1641 1642 test_letter = """ 1643 \\documentclass{letter} 1644 \\address{ $DOCTOR \\\\ 1645 $DOCTORADDRESS} 1646 \\signature{$DOCTOR} 1647 1648 \\begin{document} 1649 \\begin{letter}{$RECIPIENTNAME \\\\ 1650 $RECIPIENTADDRESS} 1651 1652 \\opening{Dear $RECIPIENTNAME} 1653 1654 \\textbf{Re:} $PATIENTNAME, DOB: $DOB, $PATIENTADDRESS \\\\ 1655 1656 $TEXT 1657 1658 \\ifnum$INCLUDEMEDS>0 1659 \\textbf{Medications List} 1660 1661 \\begin{tabular}{lll} 1662 $MEDSLIST 1663 \\end{tabular} 1664 \\fi 1665 1666 \\ifnum$INCLUDEDISEASES>0 1667 \\textbf{Disease List} 1668 1669 \\begin{tabular}{l} 1670 $DISEASELIST 1671 \\end{tabular} 1672 \\fi 1673 1674 \\closing{$CLOSING} 1675 1676 \\end{letter} 1677 \\end{document} 1678 """ 1679 1680
1681 -def test_au():
1682 f = open('../../test-area/ian/terry-form.tex') 1683 params = { 1684 'RECIPIENT': "Dr. R. Terry\n1 Main St\nNewcastle", 1685 'DOCTORSNAME': 'Ian Haywood', 1686 'DOCTORSADDRESS': '1 Smith St\nMelbourne', 1687 'PATIENTNAME':'Joe Bloggs', 1688 'PATIENTADDRESS':'18 Fred St\nMelbourne', 1689 'REQUEST':'echocardiogram', 1690 'THERAPY':'on warfarin', 1691 'CLINICALNOTES':"""heard new murmur 1692 Here's some 1693 crap to demonstrate how it can cover multiple lines.""", 1694 'COPYADDRESS':'Karsten Hilbert\nLeipzig, Germany', 1695 'ROUTINE':1, 1696 'URGENT':0, 1697 'FAX':1, 1698 'PHONE':1, 1699 'PENSIONER':1, 1700 'VETERAN':0, 1701 'PADS':0, 1702 'INSTRUCTIONS':u'Take the blue pill, Neo' 1703 } 1704 form = LaTeXForm (1, f.read()) 1705 form.process (params) 1706 form.xdvi () 1707 form.cleanup ()
1708
1709 -def test_au2 ():
1710 form = LaTeXForm (2, test_letter) 1711 params = {'RECIPIENTNAME':'Dr. Richard Terry', 1712 'RECIPIENTADDRESS':'1 Main St\nNewcastle', 1713 'DOCTOR':'Dr. Ian Haywood', 1714 'DOCTORADDRESS':'1 Smith St\nMelbourne', 1715 'PATIENTNAME':'Joe Bloggs', 1716 'PATIENTADDRESS':'18 Fred St, Melbourne', 1717 'TEXT':"""This is the main text of the referral letter""", 1718 'DOB':'12/3/65', 1719 'INCLUDEMEDS':1, 1720 'MEDSLIST':[["Amoxycillin", "500mg", "TDS"], ["Perindopril", "4mg", "OD"]], 1721 'INCLUDEDISEASES':0, 'DISEASELIST':'', 1722 'CLOSING':'Yours sincerely,' 1723 } 1724 form.process (params) 1725 print os.getcwd () 1726 form.xdvi () 1727 form.cleanup ()
1728 #------------------------------------------------------------
1729 -def test_de():
1730 template = open('../../test-area/ian/Formularkopf-DE.tex') 1731 form = LaTeXForm(template=template.read()) 1732 params = { 1733 'PATIENT LASTNAME': 'Kirk', 1734 'PATIENT FIRSTNAME': 'James T.', 1735 'PATIENT STREET': 'Hauptstrasse', 1736 'PATIENT ZIP': '02999', 1737 'PATIENT TOWN': 'Gross Saerchen', 1738 'PATIENT DOB': '22.03.1931' 1739 } 1740 form.process(params) 1741 form.xdvi() 1742 form.cleanup()
1743 1744 #============================================================ 1745 # main 1746 #------------------------------------------------------------ 1747 if __name__ == '__main__': 1748 1749 if len(sys.argv) < 2: 1750 sys.exit() 1751 1752 if sys.argv[1] != 'test': 1753 sys.exit() 1754 1755 from Gnumed.pycommon import gmDateTime 1756 gmDateTime.init() 1757 1758 #-------------------------------------------------------- 1759 # OOo 1760 #--------------------------------------------------------
1761 - def test_init_ooo():
1762 init_ooo()
1763 #--------------------------------------------------------
1764 - def test_ooo_connect():
1765 srv = gmOOoConnector() 1766 print srv 1767 print srv.desktop
1768 #--------------------------------------------------------
1769 - def test_open_ooo_doc_from_srv():
1770 srv = gmOOoConnector() 1771 doc = srv.open_document(filename = sys.argv[2]) 1772 print "document:", doc
1773 #--------------------------------------------------------
1774 - def test_open_ooo_doc_from_letter():
1775 doc = cOOoLetter(template_file = sys.argv[2]) 1776 doc.open_in_ooo() 1777 print "document:", doc 1778 raw_input('press <ENTER> to continue') 1779 doc.show() 1780 #doc.replace_placeholders() 1781 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1782 # doc = None 1783 # doc.close_in_ooo() 1784 raw_input('press <ENTER> to continue')
1785 #--------------------------------------------------------
1786 - def play_with_ooo():
1787 try: 1788 doc = open_uri_in_ooo(filename=sys.argv[1]) 1789 except: 1790 _log.exception('cannot open [%s] in OOo' % sys.argv[1]) 1791 raise 1792 1793 class myCloseListener(unohelper.Base, oooXCloseListener): 1794 def disposing(self, evt): 1795 print "disposing:"
1796 def notifyClosing(self, evt): 1797 print "notifyClosing:" 1798 def queryClosing(self, evt, owner): 1799 # owner is True/False whether I am the owner of the doc 1800 print "queryClosing:" 1801 1802 l = myCloseListener() 1803 doc.addCloseListener(l) 1804 1805 tfs = doc.getTextFields().createEnumeration() 1806 print tfs 1807 print dir(tfs) 1808 while tfs.hasMoreElements(): 1809 tf = tfs.nextElement() 1810 if tf.supportsService('com.sun.star.text.TextField.JumpEdit'): 1811 print tf.getPropertyValue('PlaceHolder') 1812 print " ", tf.getPropertyValue('Hint') 1813 1814 # doc.close(True) # closes but leaves open the dedicated OOo window 1815 doc.dispose() # closes and disposes of the OOo window 1816 #--------------------------------------------------------
1817 - def test_cOOoLetter():
1818 pat = gmPersonSearch.ask_for_patient() 1819 if pat is None: 1820 return 1821 gmPerson.set_active_patient(patient = pat) 1822 1823 doc = cOOoLetter(template_file = sys.argv[2]) 1824 doc.open_in_ooo() 1825 print doc 1826 doc.show() 1827 #doc.replace_placeholders() 1828 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1829 doc = None 1830 # doc.close_in_ooo() 1831 raw_input('press <ENTER> to continue')
1832 #-------------------------------------------------------- 1833 # other 1834 #--------------------------------------------------------
1835 - def test_cFormTemplate():
1836 template = cFormTemplate(aPK_obj = sys.argv[2]) 1837 print template 1838 print template.export_to_file()
1839 #--------------------------------------------------------
1840 - def set_template_from_file():
1841 template = cFormTemplate(aPK_obj = sys.argv[2]) 1842 template.update_template_from_file(filename = sys.argv[3])
1843 #--------------------------------------------------------
1844 - def test_latex_form():
1845 pat = gmPersonSearch.ask_for_patient() 1846 if pat is None: 1847 return 1848 gmPerson.set_active_patient(patient = pat) 1849 1850 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1851 1852 path = os.path.abspath(sys.argv[2]) 1853 form = cLaTeXForm(template_file = path) 1854 1855 from Gnumed.wxpython import gmMacro 1856 ph = gmMacro.gmPlaceholderHandler() 1857 ph.debug = True 1858 instance_file = form.substitute_placeholders(data_source = ph) 1859 pdf_name = form.generate_output(instance_file = instance_file) 1860 print "final PDF file is:", pdf_name
1861 #--------------------------------------------------------
1862 - def test_pdf_form():
1863 pat = gmPersonSearch.ask_for_patient() 1864 if pat is None: 1865 return 1866 gmPerson.set_active_patient(patient = pat) 1867 1868 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1869 1870 path = os.path.abspath(sys.argv[2]) 1871 form = cPDFForm(template_file = path) 1872 1873 from Gnumed.wxpython import gmMacro 1874 ph = gmMacro.gmPlaceholderHandler() 1875 ph.debug = True 1876 instance_file = form.substitute_placeholders(data_source = ph) 1877 pdf_name = form.generate_output(instance_file = instance_file) 1878 print "final PDF file is:", pdf_name
1879 #--------------------------------------------------------
1880 - def test_abiword_form():
1881 pat = gmPersonSearch.ask_for_patient() 1882 if pat is None: 1883 return 1884 gmPerson.set_active_patient(patient = pat) 1885 1886 gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 1887 1888 path = os.path.abspath(sys.argv[2]) 1889 form = cAbiWordForm(template_file = path) 1890 1891 from Gnumed.wxpython import gmMacro 1892 ph = gmMacro.gmPlaceholderHandler() 1893 ph.debug = True 1894 instance_file = form.substitute_placeholders(data_source = ph) 1895 form.edit() 1896 final_name = form.generate_output(instance_file = instance_file) 1897 print "final file is:", final_name
1898 #-------------------------------------------------------- 1899 #-------------------------------------------------------- 1900 # now run the tests 1901 #test_au() 1902 #test_de() 1903 1904 # OOo 1905 #test_init_ooo() 1906 #test_ooo_connect() 1907 #test_open_ooo_doc_from_srv() 1908 #test_open_ooo_doc_from_letter() 1909 #play_with_ooo() 1910 #test_cOOoLetter() 1911 1912 #test_cFormTemplate() 1913 #set_template_from_file() 1914 #test_latex_form() 1915 #test_pdf_form() 1916 test_abiword_form() 1917 1918 #============================================================ 1919