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 = '%s -invisible -norestore -accept="pipe,name=%s;urp"' % ( 408 self.ooo_start_cmd = '%s --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.""" 706835 #------------------------------------------------------------ 836 form_engines[u'L'] = cLaTeXForm 837 #============================================================ 838 # Gnuplot template forms 839 #------------------------------------------------------------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 try: 730 val = data_source[placeholder] 731 except: 732 _log.exception(val) 733 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder) 734 735 if val is None: 736 val = _('error with placeholder [%s]') % gmTools.tex_escape_string(placeholder) 737 738 line = line.replace(placeholder, val) 739 740 instance_file.write(line) 741 742 instance_file.close() 743 template_file.close() 744 745 return746 #--------------------------------------------------------748 749 mimetypes = [ 750 u'application/x-latex', 751 u'application/x-tex', 752 u'text/plain' 753 ] 754 755 for mimetype in mimetypes: 756 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.instance_filename) 757 if editor_cmd is not None: 758 break 759 760 if editor_cmd is None: 761 editor_cmd = u'sensible-editor %s' % self.instance_filename 762 763 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 764 self.re_editable_filenames = [self.instance_filename] 765 766 return result767 #--------------------------------------------------------769 770 if instance_file is None: 771 instance_file = self.instance_filename 772 773 try: 774 open(instance_file, 'r').close() 775 except: 776 _log.exception('cannot access form instance file [%s]', instance_file) 777 gmLog2.log_stack_trace() 778 return None 779 780 self.instance_filename = instance_file 781 782 _log.debug('ignoring <format> directive [%s], generating PDF', format) 783 784 # create sandbox for LaTeX to play in 785 sandbox_dir = os.path.splitext(self.template_filename)[0] 786 _log.debug('LaTeX sandbox directory: [%s]', sandbox_dir) 787 788 old_cwd = os.getcwd() 789 _log.debug('CWD: [%s]', old_cwd) 790 791 gmTools.mkdir(sandbox_dir) 792 793 os.chdir(sandbox_dir) 794 try: 795 sandboxed_instance_filename = os.path.join(sandbox_dir, os.path.split(self.instance_filename)[1]) 796 shutil.move(self.instance_filename, sandboxed_instance_filename) 797 798 # LaTeX can need up to three runs to get cross references et al right 799 if platform.system() == 'Windows': 800 draft_cmd = r'pdflatex.exe -draftmode -interaction nonstopmode %s' % sandboxed_instance_filename 801 final_cmd = r'pdflatex.exe -interaction nonstopmode %s' % sandboxed_instance_filename 802 else: 803 draft_cmd = r'pdflatex -draftmode -interaction nonstopmode %s' % sandboxed_instance_filename 804 final_cmd = r'pdflatex -interaction nonstopmode %s' % sandboxed_instance_filename 805 for run_cmd in [draft_cmd, draft_cmd, final_cmd]: 806 if not gmShellAPI.run_command_in_shell(command = run_cmd, blocking = True, acceptable_return_codes = [0, 1]): 807 _log.error('problem running pdflatex, cannot generate form output') 808 gmDispatcher.send(signal = 'statustext', msg = _('Error running pdflatex. Cannot turn LaTeX template into PDF.'), beep = True) 809 os.chdir(old_cwd) 810 return None 811 finally: 812 os.chdir(old_cwd) 813 814 sandboxed_pdf_name = u'%s.pdf' % os.path.splitext(sandboxed_instance_filename)[0] 815 target_dir = os.path.split(self.instance_filename)[0] 816 try: 817 shutil.move(sandboxed_pdf_name, target_dir) 818 except IOError: 819 _log.exception('cannot move sandboxed PDF: %s -> %s', sandboxed_pdf_name, target_dir) 820 gmDispatcher.send(signal = 'statustext', msg = _('Sandboxed PDF output file cannot be moved.'), beep = True) 821 return None 822 823 final_pdf_name = u'%s.pdf' % os.path.splitext(self.instance_filename)[0] 824 825 try: 826 open(final_pdf_name, 'r').close() 827 except IOError: 828 _log.exception('cannot open target PDF: %s', final_pdf_name) 829 gmDispatcher.send(signal = 'statustext', msg = _('PDF output file cannot be opened.'), beep = True) 830 return None 831 832 self.final_output_filenames = [final_pdf_name] 833 834 return final_pdf_name841 """A forms engine wrapping Gnuplot.""" 842 843 #-------------------------------------------------------- 847 #--------------------------------------------------------892 #------------------------------------------------------------ 893 form_engines[u'G'] = cGnuplotForm 894 895 #============================================================ 896 # fPDF form engine 897 #------------------------------------------------------------849 """Allow editing the instance of the template.""" 850 self.re_editable_filenames = [] 851 return True852 #--------------------------------------------------------854 """Generate output suitable for further processing outside this class, e.g. printing. 855 856 Expects .data_filename to be set. 857 """ 858 self.conf_filename = gmTools.get_unique_filename(prefix = 'gm2gpl-', suffix = '.conf') 859 fname_file = codecs.open(self.conf_filename, 'wb', 'utf8') 860 fname_file.write('# setting the gnuplot data file\n') 861 fname_file.write("gm2gpl_datafile = '%s'\n" % self.data_filename) 862 fname_file.close() 863 864 # FIXME: cater for configurable path 865 if platform.system() == 'Windows': 866 exec_name = 'gnuplot.exe' 867 else: 868 exec_name = 'gnuplot' 869 870 args = [exec_name, '-p', self.conf_filename, self.template_filename] 871 _log.debug('plotting args: %s' % str(args)) 872 873 try: 874 gp = subprocess.Popen ( 875 args = args, 876 close_fds = True 877 ) 878 except (OSError, ValueError, subprocess.CalledProcessError): 879 _log.exception('there was a problem executing gnuplot') 880 gmDispatcher.send(signal = u'statustext', msg = _('Error running gnuplot. Cannot plot data.'), beep = True) 881 return 882 883 gp.communicate() 884 885 self.final_output_filenames = [ 886 self.conf_filename, 887 self.data_filename, 888 self.template_filename 889 ] 890 891 return899 """A forms engine wrapping PDF forms. 900 901 Johann Felix Soden <johfel@gmx.de> helped with this. 902 903 http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf 904 905 http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/fdf_data_exchange.pdf 906 """ 9071104 #------------------------------------------------------------ 1105 form_engines[u'P'] = cPDFForm 1106 1107 #============================================================ 1108 # older code 1109 #------------------------------------------------------------909 910 super(cPDFForm, self).__init__(template_file = template_file) 911 912 # detect pdftk 913 found, self.pdftk_binary = gmShellAPI.detect_external_binary(binary = r'pdftk') 914 if not found: 915 raise ImportError('<pdftk(.exe)> not found') 916 return # should be superfluous, actually 917 918 enc = sys.getfilesystemencoding() 919 self.pdftk_binary = self.pdftk_binary.encode(enc) 920 921 base_name, ext = os.path.splitext(self.template_filename) 922 self.fdf_dumped_filename = (u'%s.fdf' % base_name).encode(enc) 923 self.fdf_replaced_filename = (u'%s-replaced.fdf' % base_name).encode(enc) 924 self.pdf_filled_filename = (u'%s-filled.pdf' % base_name).encode(enc) 925 self.pdf_flattened_filename = (u'%s-filled-flattened.pdf' % base_name).encode(enc)926 #--------------------------------------------------------928 929 # dump form fields from template 930 cmd_line = [ 931 self.pdftk_binary, 932 self.template_filename, 933 r'generate_fdf', 934 r'output', 935 self.fdf_dumped_filename 936 ] 937 _log.debug(u' '.join(cmd_line)) 938 try: 939 pdftk = subprocess.Popen(cmd_line) 940 except OSError: 941 _log.exception('cannot run <pdftk> (dump data from form)') 942 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot extract fields from PDF form template.'), beep = True) 943 return False 944 945 pdftk.communicate() 946 if pdftk.returncode != 0: 947 _log.error('<pdftk> returned [%s], failed to dump data from PDF form into FDF', pdftk.returncode) 948 return False 949 950 # parse dumped FDF file for "/V (...)" records 951 # and replace placeholders therein 952 fdf_dumped_file = open(self.fdf_dumped_filename, 'rbU') 953 fdf_replaced_file = codecs.open(self.fdf_replaced_filename, 'wb') 954 955 string_value_regex = r'\s*/V\s*\(.+\)\s*$' 956 for line in fdf_dumped_file: 957 if not regex.match(string_value_regex, line): 958 fdf_replaced_file.write(line) 959 continue 960 961 # strip cruft around the string value 962 raw_str_val = line.strip() # remove framing whitespace 963 raw_str_val = raw_str_val[2:] # remove leading "/V" 964 raw_str_val = raw_str_val.lstrip() # remove whitespace between "/V" and "(" 965 raw_str_val = raw_str_val[1:] # remove opening "(" 966 raw_str_val = raw_str_val[2:] # remove BOM-16-BE 967 raw_str_val = raw_str_val.rstrip() # remove trailing whitespace 968 raw_str_val = raw_str_val[:-1] # remove closing ")" 969 970 # work on FDF escapes 971 raw_str_val = raw_str_val.replace('\(', '(') # remove escaping of "(" 972 raw_str_val = raw_str_val.replace('\)', ')') # remove escaping of ")" 973 974 # by now raw_str_val should contain the actual 975 # string value, albeit encoded as UTF-16, so 976 # decode it into a unicode object, 977 # split multi-line fields on "\n" literal 978 raw_str_lines = raw_str_val.split('\x00\\n') 979 value_template_lines = [] 980 for raw_str_line in raw_str_lines: 981 value_template_lines.append(raw_str_line.decode('utf_16_be')) 982 983 replaced_lines = [] 984 for value_template in value_template_lines: 985 # find any placeholders within 986 placeholders_in_value = regex.findall(data_source.placeholder_regex, value_template, regex.IGNORECASE) 987 for placeholder in placeholders_in_value: 988 try: 989 replacement = data_source[placeholder] 990 except: 991 _log.exception(replacement) 992 replacement = _('error with placeholder [%s]') % placeholder 993 if replacement is None: 994 replacement = _('error with placeholder [%s]') % placeholder 995 value_template = value_template.replace(placeholder, replacement) 996 997 value_template = value_template.encode('utf_16_be') 998 999 if len(placeholders_in_value) > 0: 1000 value_template = value_template.replace(r'(', r'\(') 1001 value_template = value_template.replace(r')', r'\)') 1002 1003 replaced_lines.append(value_template) 1004 1005 replaced_line = '\x00\\n'.join(replaced_lines) 1006 1007 fdf_replaced_file.write('/V (') 1008 fdf_replaced_file.write(codecs.BOM_UTF16_BE) 1009 fdf_replaced_file.write(replaced_line) 1010 fdf_replaced_file.write(')\n') 1011 1012 fdf_replaced_file.close() 1013 fdf_dumped_file.close() 1014 1015 # merge replaced data back into form 1016 cmd_line = [ 1017 self.pdftk_binary, 1018 self.template_filename, 1019 r'fill_form', 1020 self.fdf_replaced_filename, 1021 r'output', 1022 self.pdf_filled_filename 1023 ] 1024 _log.debug(u' '.join(cmd_line)) 1025 try: 1026 pdftk = subprocess.Popen(cmd_line) 1027 except OSError: 1028 _log.exception('cannot run <pdftk> (merge data into form)') 1029 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot fill in PDF form template.'), beep = True) 1030 return False 1031 1032 pdftk.communicate() 1033 if pdftk.returncode != 0: 1034 _log.error('<pdftk> returned [%s], failed to merge FDF data into PDF form', pdftk.returncode) 1035 return False 1036 1037 return True1038 #--------------------------------------------------------1040 mimetypes = [ 1041 u'application/pdf', 1042 u'application/x-pdf' 1043 ] 1044 1045 for mimetype in mimetypes: 1046 editor_cmd = gmMimeLib.get_editor_cmd(mimetype, self.pdf_filled_filename) 1047 if editor_cmd is not None: 1048 break 1049 1050 if editor_cmd is None: 1051 _log.debug('editor cmd not found, trying viewer cmd') 1052 for mimetype in mimetypes: 1053 editor_cmd = gmMimeLib.get_viewer_cmd(mimetype, self.pdf_filled_filename) 1054 if editor_cmd is not None: 1055 break 1056 1057 if editor_cmd is None: 1058 return False 1059 1060 result = gmShellAPI.run_command_in_shell(command = editor_cmd, blocking = True) 1061 1062 path, fname = os.path.split(self.pdf_filled_filename) 1063 candidate = os.path.join(gmTools.gmPaths().home_dir, fname) 1064 1065 if os.access(candidate, os.R_OK): 1066 _log.debug('filled-in PDF found: %s', candidate) 1067 os.rename(self.pdf_filled_filename, self.pdf_filled_filename + '.bak') 1068 shutil.move(candidate, path) 1069 else: 1070 _log.debug('filled-in PDF not found: %s', candidate) 1071 1072 self.re_editable_filenames = [self.pdf_filled_filename] 1073 1074 return result1075 #--------------------------------------------------------1077 """Generate output suitable for further processing outside this class, e.g. printing.""" 1078 1079 # eventually flatten the filled in form so we 1080 # can keep both a flattened and an editable copy: 1081 cmd_line = [ 1082 self.pdftk_binary, 1083 self.pdf_filled_filename, 1084 r'output', 1085 self.pdf_flattened_filename, 1086 r'flatten' 1087 ] 1088 _log.debug(u' '.join(cmd_line)) 1089 try: 1090 pdftk = subprocess.Popen(cmd_line) 1091 except OSError: 1092 _log.exception('cannot run <pdftk> (flatten filled in form)') 1093 gmDispatcher.send(signal = u'statustext', msg = _('Error running pdftk. Cannot flatten filled in PDF form.'), beep = True) 1094 return None 1095 1096 pdftk.communicate() 1097 if pdftk.returncode != 0: 1098 _log.error('<pdftk> returned [%s], failed to flatten filled in PDF form', pdftk.returncode) 1099 return None 1100 1101 self.final_output_filenames = [self.pdf_flattened_filename] 1102 1103 return self.pdf_flattened_filename1111 """A forms engine wrapping LaTeX. 1112 """ 11161169 1170 1171 1172 1173 #================================================================ 1174 # define a class for HTML forms (for printing) 1175 #================================================================1118 try: 1119 latex = Cheetah.Template.Template (self.template, filter=LaTeXFilter, searchList=[params]) 1120 # create a 'sandbox' directory for LaTeX to play in 1121 self.tmp = tempfile.mktemp () 1122 os.makedirs (self.tmp) 1123 self.oldcwd = os.getcwd () 1124 os.chdir (self.tmp) 1125 stdin = os.popen ("latex", "w", 2048) 1126 stdin.write (str (latex)) #send text. LaTeX spits it's output into stdout 1127 # FIXME: send LaTeX output to the logger 1128 stdin.close () 1129 if not gmShellAPI.run_command_in_shell("dvips texput.dvi -o texput.ps", blocking=True): 1130 raise FormError ('DVIPS returned error') 1131 except EnvironmentError, e: 1132 _log.error(e.strerror) 1133 raise FormError (e.strerror) 1134 return file ("texput.ps")11351137 """ 1138 For testing purposes, runs Xdvi on the intermediate TeX output 1139 WARNING: don't try this on Windows 1140 """ 1141 gmShellAPI.run_command_in_shell("xdvi texput.dvi", blocking=True)11421144 if "%F" in command: 1145 command.replace ("%F", "texput.ps") 1146 else: 1147 command = "%s < texput.ps" % command 1148 try: 1149 if not gmShellAPI.run_command_in_shell(command, blocking=True): 1150 _log.error("external command %s returned non-zero" % command) 1151 raise FormError ('external command %s returned error' % command) 1152 except EnvironmentError, e: 1153 _log.error(e.strerror) 1154 raise FormError (e.strerror) 1155 return True11561158 command, set1 = gmCfg.getDBParam (workplace = self.workplace, option = 'main.comms.print') 1159 self.exe (command)11601177 """This class can create XML document from requested data, 1178 then process it with XSLT template and display results 1179 """ 1180 1181 # FIXME: make the path configurable ? 1182 _preview_program = u'oowriter ' #this program must be in the system PATH 11831260 1261 1262 #===================================================== 1263 #class LaTeXFilter(Cheetah.Filters.Filter):1185 1186 if template is None: 1187 raise ValueError(u'%s: cannot create form instance without a template' % __name__) 1188 1189 cFormEngine.__init__(self, template = template) 1190 1191 self._FormData = None 1192 1193 # here we know/can assume that the template was stored as a utf-8 1194 # encoded string so use that conversion to create unicode: 1195 #self._XSLTData = unicode(str(template.template_data), 'UTF-8') 1196 # but in fact, unicode() knows how to handle buffers, so simply: 1197 self._XSLTData = unicode(self.template.template_data, 'UTF-8', 'strict') 1198 1199 # we must still devise a method of extracting the SQL query: 1200 # - either by retrieving it from a particular tag in the XSLT or 1201 # - by making the stored template actually be a dict which, unpickled, 1202 # has the keys "xslt" and "sql" 1203 self._SQL_query = u'select 1' #this sql query must output valid xml1204 #-------------------------------------------------------- 1205 # external API 1206 #--------------------------------------------------------1208 """get data from backend and process it with XSLT template to produce readable output""" 1209 1210 # extract SQL (this is wrong but displays what is intended) 1211 xslt = libxml2.parseDoc(self._XSLTData) 1212 root = xslt.children 1213 for child in root: 1214 if child.type == 'element': 1215 self._SQL_query = child.content 1216 break 1217 1218 # retrieve data from backend 1219 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': self._SQL_query, 'args': sql_parameters}], get_col_idx = False) 1220 1221 __header = '<?xml version="1.0" encoding="UTF-8"?>\n' 1222 __body = rows[0][0] 1223 1224 # process XML data according to supplied XSLT, producing HTML 1225 self._XMLData =__header + __body 1226 style = libxslt.parseStylesheetDoc(xslt) 1227 xml = libxml2.parseDoc(self._XMLData) 1228 html = style.applyStylesheet(xml, None) 1229 self._FormData = html.serialize() 1230 1231 style.freeStylesheet() 1232 xml.freeDoc() 1233 html.freeDoc()1234 #--------------------------------------------------------1236 if self._FormData is None: 1237 raise ValueError, u'Preview request for empty form. Make sure the form is properly initialized and process() was performed' 1238 1239 fname = gmTools.get_unique_filename(prefix = u'gm_XSLT_form-', suffix = u'.html') 1240 #html_file = os.open(fname, 'wb') 1241 #html_file.write(self._FormData.encode('UTF-8')) 1242 html_file = codecs.open(fname, 'wb', 'utf8', 'strict') # or 'replace' ? 1243 html_file.write(self._FormData) 1244 html_file.close() 1245 1246 cmd = u'%s %s' % (self.__class__._preview_program, fname) 1247 1248 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = False): 1249 _log.error('%s: cannot launch report preview program' % __name__) 1250 return False 1251 1252 #os.unlink(self.filename) #delete file 1253 #FIXME: under Windows the temp file is deleted before preview program gets it (under Linux it works OK) 1254 1255 return True1256 #--------------------------------------------------------1303 1304 1305 #=========================================================== 1308 1309 #============================================================ 1310 # convenience functions 1311 #------------------------------------------------------------1266 """ 1267 Convience function to escape ISO-Latin-1 strings for TeX output 1268 WARNING: not all ISO-Latin-1 characters are expressible in TeX 1269 FIXME: nevertheless, there are a few more we could support 1270 1271 Also intelligently convert lists and tuples into TeX-style table lines 1272 """ 1273 if type (item) is types.UnicodeType or type (item) is types.StringType: 1274 item = item.replace ("\\", "\\backslash") # I wonder about this, do we want users to be able to use raw TeX? 1275 item = item.replace ("&", "\\&") 1276 item = item.replace ("$", "\\$") 1277 item = item.replace ('"', "") # okay, that's not right, but easiest solution for now 1278 item = item.replace ("\n", "\\\\ ") 1279 if len (item.strip ()) == 0: 1280 item = "\\relax " # sometimes TeX really hates empty strings, this seems to mollify it 1281 # FIXME: cover all of ISO-Latin-1 which can be expressed in TeX 1282 if type (item) is types.UnicodeType: 1283 item = item.encode ('latin-1', 'replace') 1284 trans = {'ß':'\\ss{}', 'ä': '\\"{a}', 'Ä' :'\\"{A}', 'ö': '\\"{o}', 'Ö': '\\"{O}', 'ü': '\\"{u}', 'Ü': '\\"{U}', 1285 '\x8a':'\\v{S}', '\x8a':'\\OE{}', '\x9a':'\\v{s}', '\x9c': '\\oe{}', '\a9f':'\\"{Y}', #Microsloth extensions 1286 '\x86': '{\\dag}', '\x87': '{\\ddag}', '\xa7':'{\\S}', '\xb6': '{\\P}', '\xa9': '{\\copyright}', '\xbf': '?`', 1287 '\xc0':'\\`{A}', '\xa1': "\\'{A}", '\xa2': '\\^{A}', '\xa3':'\\~{A}', '\\xc5': '{\AA}', 1288 '\xc7':'\\c{C}', '\xc8':'\\`{E}', 1289 '\xa1': '!`', 1290 '\xb5':'$\mu$', '\xa3': '\pounds{}', '\xa2':'cent'} 1291 for k, i in trans.items (): 1292 item = item.replace (k, i) 1293 elif type (item) is types.ListType or type (item) is types.TupleType: 1294 item = string.join ([self.filter (i, ' & ') for i in item], table_sep) 1295 elif item is None: 1296 item = '\\relax % Python None\n' 1297 elif type (item) is types.IntType or type (item) is types.FloatType: 1298 item = str (item) 1299 else: 1300 item = str (item) 1301 _log.warning("unknown type %s, string %s" % (type (item), item)) 1302 return item1313 """ 1314 Instantiates a FormEngine based on the form ID or name from the backend 1315 """ 1316 try: 1317 # it's a number: match to form ID 1318 id = int (id) 1319 cmd = 'select template, engine, pk from paperwork_templates where pk = %s' 1320 except ValueError: 1321 # it's a string, match to the form's name 1322 # FIXME: can we somehow OR like this: where name_short=%s OR name_long=%s ? 1323 cmd = 'select template, engine, flags, pk from paperwork_templates where name_short = %s' 1324 result = gmPG.run_ro_query ('reference', cmd, None, id) 1325 if result is None: 1326 _log.error('error getting form [%s]' % id) 1327 raise gmExceptions.FormError ('error getting form [%s]' % id) 1328 if len(result) == 0: 1329 _log.error('no form [%s] found' % id) 1330 raise gmExceptions.FormError ('no such form found [%s]' % id) 1331 if result[0][1] == 'L': 1332 return LaTeXForm (result[0][2], result[0][0]) 1333 elif result[0][1] == 'T': 1334 return TextForm (result[0][2], result[0][0]) 1335 else: 1336 _log.error('no form engine [%s] for form [%s]' % (result[0][1], id)) 1337 raise FormError ('no engine [%s] for form [%s]' % (result[0][1], id))1338 #------------------------------------------------------------- 1345 #------------------------------------------------------------- 1346 1347 test_letter = """ 1348 \\documentclass{letter} 1349 \\address{ $DOCTOR \\\\ 1350 $DOCTORADDRESS} 1351 \\signature{$DOCTOR} 1352 1353 \\begin{document} 1354 \\begin{letter}{$RECIPIENTNAME \\\\ 1355 $RECIPIENTADDRESS} 1356 1357 \\opening{Dear $RECIPIENTNAME} 1358 1359 \\textbf{Re:} $PATIENTNAME, DOB: $DOB, $PATIENTADDRESS \\\\ 1360 1361 $TEXT 1362 1363 \\ifnum$INCLUDEMEDS>0 1364 \\textbf{Medications List} 1365 1366 \\begin{tabular}{lll} 1367 $MEDSLIST 1368 \\end{tabular} 1369 \\fi 1370 1371 \\ifnum$INCLUDEDISEASES>0 1372 \\textbf{Disease List} 1373 1374 \\begin{tabular}{l} 1375 $DISEASELIST 1376 \\end{tabular} 1377 \\fi 1378 1379 \\closing{$CLOSING} 1380 1381 \\end{letter} 1382 \\end{document} 1383 """ 1384 13851387 f = open('../../test-area/ian/terry-form.tex') 1388 params = { 1389 'RECIPIENT': "Dr. R. Terry\n1 Main St\nNewcastle", 1390 'DOCTORSNAME': 'Ian Haywood', 1391 'DOCTORSADDRESS': '1 Smith St\nMelbourne', 1392 'PATIENTNAME':'Joe Bloggs', 1393 'PATIENTADDRESS':'18 Fred St\nMelbourne', 1394 'REQUEST':'echocardiogram', 1395 'THERAPY':'on warfarin', 1396 'CLINICALNOTES':"""heard new murmur 1397 Here's some 1398 crap to demonstrate how it can cover multiple lines.""", 1399 'COPYADDRESS':'Karsten Hilbert\nLeipzig, Germany', 1400 'ROUTINE':1, 1401 'URGENT':0, 1402 'FAX':1, 1403 'PHONE':1, 1404 'PENSIONER':1, 1405 'VETERAN':0, 1406 'PADS':0, 1407 'INSTRUCTIONS':u'Take the blue pill, Neo' 1408 } 1409 form = LaTeXForm (1, f.read()) 1410 form.process (params) 1411 form.xdvi () 1412 form.cleanup ()14131415 form = LaTeXForm (2, test_letter) 1416 params = {'RECIPIENTNAME':'Dr. Richard Terry', 1417 'RECIPIENTADDRESS':'1 Main St\nNewcastle', 1418 'DOCTOR':'Dr. Ian Haywood', 1419 'DOCTORADDRESS':'1 Smith St\nMelbourne', 1420 'PATIENTNAME':'Joe Bloggs', 1421 'PATIENTADDRESS':'18 Fred St, Melbourne', 1422 'TEXT':"""This is the main text of the referral letter""", 1423 'DOB':'12/3/65', 1424 'INCLUDEMEDS':1, 1425 'MEDSLIST':[["Amoxycillin", "500mg", "TDS"], ["Perindopril", "4mg", "OD"]], 1426 'INCLUDEDISEASES':0, 'DISEASELIST':'', 1427 'CLOSING':'Yours sincerely,' 1428 } 1429 form.process (params) 1430 print os.getcwd () 1431 form.xdvi () 1432 form.cleanup ()1433 #------------------------------------------------------------1435 template = open('../../test-area/ian/Formularkopf-DE.tex') 1436 form = LaTeXForm(template=template.read()) 1437 params = { 1438 'PATIENT LASTNAME': 'Kirk', 1439 'PATIENT FIRSTNAME': 'James T.', 1440 'PATIENT STREET': 'Hauptstrasse', 1441 'PATIENT ZIP': '02999', 1442 'PATIENT TOWN': 'Gross Saerchen', 1443 'PATIENT DOB': '22.03.1931' 1444 } 1445 form.process(params) 1446 form.xdvi() 1447 form.cleanup()1448 1449 #============================================================ 1450 # main 1451 #------------------------------------------------------------ 1452 if __name__ == '__main__': 1453 1454 if len(sys.argv) < 2: 1455 sys.exit() 1456 1457 if sys.argv[1] != 'test': 1458 sys.exit() 1459 1460 from Gnumed.pycommon import gmI18N, gmDateTime 1461 gmI18N.activate_locale() 1462 gmI18N.install_domain(domain='gnumed') 1463 gmDateTime.init() 1464 1465 #-------------------------------------------------------- 1466 # OOo 1467 #--------------------------------------------------------1469 init_ooo()1470 #-------------------------------------------------------- 1475 #--------------------------------------------------------1477 srv = gmOOoConnector() 1478 doc = srv.open_document(filename = sys.argv[2]) 1479 print "document:", doc1480 #--------------------------------------------------------1482 doc = cOOoLetter(template_file = sys.argv[2]) 1483 doc.open_in_ooo() 1484 print "document:", doc 1485 raw_input('press <ENTER> to continue') 1486 doc.show() 1487 #doc.replace_placeholders() 1488 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1489 # doc = None 1490 # doc.close_in_ooo() 1491 raw_input('press <ENTER> to continue')1492 #--------------------------------------------------------1494 try: 1495 doc = open_uri_in_ooo(filename=sys.argv[1]) 1496 except: 1497 _log.exception('cannot open [%s] in OOo' % sys.argv[1]) 1498 raise 1499 1500 class myCloseListener(unohelper.Base, oooXCloseListener): 1501 def disposing(self, evt): 1502 print "disposing:"1503 def notifyClosing(self, evt): 1504 print "notifyClosing:" 1505 def queryClosing(self, evt, owner): 1506 # owner is True/False whether I am the owner of the doc 1507 print "queryClosing:" 1508 1509 l = myCloseListener() 1510 doc.addCloseListener(l) 1511 1512 tfs = doc.getTextFields().createEnumeration() 1513 print tfs 1514 print dir(tfs) 1515 while tfs.hasMoreElements(): 1516 tf = tfs.nextElement() 1517 if tf.supportsService('com.sun.star.text.TextField.JumpEdit'): 1518 print tf.getPropertyValue('PlaceHolder') 1519 print " ", tf.getPropertyValue('Hint') 1520 1521 # doc.close(True) # closes but leaves open the dedicated OOo window 1522 doc.dispose() # closes and disposes of the OOo window 1523 #--------------------------------------------------------1525 pat = gmPersonSearch.ask_for_patient() 1526 if pat is None: 1527 return 1528 gmPerson.set_active_patient(patient = pat) 1529 1530 doc = cOOoLetter(template_file = sys.argv[2]) 1531 doc.open_in_ooo() 1532 print doc 1533 doc.show() 1534 #doc.replace_placeholders() 1535 #doc.save_in_ooo('~/test_cOOoLetter.odt') 1536 doc = None 1537 # doc.close_in_ooo() 1538 raw_input('press <ENTER> to continue')1539 #-------------------------------------------------------- 1540 # other 1541 #--------------------------------------------------------1543 template = cFormTemplate(aPK_obj = sys.argv[2]) 1544 print template 1545 print template.export_to_file()1546 #--------------------------------------------------------1548 template = cFormTemplate(aPK_obj = sys.argv[2]) 1549 template.update_template_from_file(filename = sys.argv[3])1550 #--------------------------------------------------------1552 pat = gmPersonSearch.ask_for_patient() 1553 if pat is None: 1554 return 1555 gmPerson.set_active_patient(patient = pat) 1556 1557 gmPerson.gmCurrentProvider(provider = gmPerson.cStaff()) 1558 1559 path = os.path.abspath(sys.argv[2]) 1560 form = cLaTeXForm(template_file = path) 1561 1562 from Gnumed.wxpython import gmMacro 1563 ph = gmMacro.gmPlaceholderHandler() 1564 ph.debug = True 1565 instance_file = form.substitute_placeholders(data_source = ph) 1566 pdf_name = form.generate_output(instance_file = instance_file) 1567 print "final PDF file is:", pdf_name1568 #--------------------------------------------------------1570 pat = gmPersonSearch.ask_for_patient() 1571 if pat is None: 1572 return 1573 gmPerson.set_active_patient(patient = pat) 1574 1575 gmPerson.gmCurrentProvider(provider = gmPerson.cStaff()) 1576 1577 path = os.path.abspath(sys.argv[2]) 1578 form = cLaPDFForm(template_file = path) 1579 1580 from Gnumed.wxpython import gmMacro 1581 ph = gmMacro.gmPlaceholderHandler() 1582 ph.debug = True 1583 instance_file = form.substitute_placeholders(data_source = ph) 1584 pdf_name = form.generate_output(instance_file = instance_file) 1585 print "final PDF file is:", pdf_name1586 #-------------------------------------------------------- 1587 #-------------------------------------------------------- 1588 # now run the tests 1589 #test_au() 1590 #test_de() 1591 1592 # OOo 1593 #test_init_ooo() 1594 #test_ooo_connect() 1595 #test_open_ooo_doc_from_srv() 1596 #test_open_ooo_doc_from_letter() 1597 #play_with_ooo() 1598 #test_cOOoLetter() 1599 1600 #test_cFormTemplate() 1601 #set_template_from_file() 1602 #test_latex_form() 1603 test_pdf_form() 1604 1605 #============================================================ 1606
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Tue Oct 18 04:00:26 2011 | http://epydoc.sourceforge.net |