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