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