| Home | Trees | Indices | Help |
|
|---|
|
|
1 #!/usr/bin/env python
2
3 __doc__ = """GNUmed web client launcher.
4 """
5 #==========================================================
6 # $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/client/wxpython/gnumed.py,v $
7 # $Id: gnumed.py,v 1.169 2010-01-31 18:20:41 ncq Exp $
8 __version__ = "$Revision: 1 $"
9 __author__ = "S. Hilbert <Sebastian.Hilbert@gmx.net>"
10 __license__ = "GPL (details at http://www.gnu.org)"
11
12 import cherrypy # importing the CherryPy server library
13 from Cheetah.Template import Template # importing the Cheetah Template engine
14
15 try:
16 from json import loads, dumps
17 except ImportError:
18 from simplejson import loads, dumps
19
20
21
22 # stdlib
23 import sys, time, os, cPickle, zlib, locale, os.path, datetime as pyDT, webbrowser, shutil, logging, urllib2, re as regex
24
25 # GNUmed libs
26 from Gnumed.pycommon import gmI18N, gmTools, gmDateTime, gmHooks
27 from Gnumed.pycommon import gmLoginInfo, gmPG2, gmBackendListener, gmTools, gmCfg2, gmI18N, gmDispatcher
28
29 from Gnumed.business import gmDocuments
30 from Gnumed.CherryPy import gmGuiHelpersWeb
31
32 #try:
33 # _('dummy-no-need-to-translate-but-make-epydoc-happy')
34 #except NameError:
35 # _ = lambda x:x
36
37 _cfg = gmCfg2.gmCfgData()
38 _provider = None
39 _scripting_listener = None
40
41 _log = logging.getLogger('gm.main')
42 _log.info(__version__)
43 _log.info('web GUI framework')
44
45 #================================================================
46 # convenience functions
47 #----------------------------------------------------------------
48 -def connect_to_database(login_info=None, max_attempts=3, expected_version=None, require_version=True):
49 """Display the login dialog and try to log into the backend.
50
51 - up to max_attempts times
52 - returns True/False
53 """
54 # force programmer to set a valid expected_version
55 expected_hash = gmPG2.known_schema_hashes[expected_version]
56 client_version = _cfg.get(option = u'client_version')
57 global current_db_name
58 current_db_name = u'gnumed_%s' % expected_version
59
60 attempt = 0
61
62 while attempt < max_attempts:
63
64 _log.debug('login attempt %s of %s', (attempt+1), max_attempts)
65
66 connected = False
67
68 login = login_info
69 if login is None:
70 _log.info("did not provide a login information")
71
72 # try getting a connection to verify the DSN works
73 dsn = gmPG2.make_psycopg2_dsn (
74 database = login.database,
75 host = login.host,
76 port = login.port,
77 user = login.user,
78 password = login.password
79 )
80 try:
81 conn = gmPG2.get_raw_connection(dsn = dsn, verbose = True, readonly = True)
82 connected = True
83
84 except gmPG2.cAuthenticationError, e:
85 attempt += 1
86 _log.error(u"login attempt failed: %s", e)
87 if attempt < max_attempts:
88 if (u'host=127.0.0.1' in (u'%s' % e)) or (u'host=' not in (u'%s' % e)):
89 msg = _(
90 'Unable to connect to database:\n\n'
91 '%s\n\n'
92 "Are you sure you have got a local database installed ?\n"
93 '\n'
94 "Please retry with proper credentials or cancel.\n"
95 '\n'
96 'You may also need to check the PostgreSQL client\n'
97 'authentication configuration in pg_hba.conf. For\n'
98 'details see:\n'
99 '\n'
100 'wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL'
101 )
102 else:
103 msg = _(
104 "Unable to connect to database:\n\n"
105 "%s\n\n"
106 "Please retry with proper credentials or cancel.\n"
107 "\n"
108 'You may also need to check the PostgreSQL client\n'
109 'authentication configuration in pg_hba.conf. For\n'
110 'details see:\n'
111 '\n'
112 'wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL'
113 )
114 msg = msg % e
115 msg = regex.sub(r'password=[^\s]+', u'password=%s' % gmTools.u_replacement_character, msg)
116 gmGuiHelpersWeb.gm_show_error (
117 msg,
118 _('Connecting to backend')
119 )
120 del e
121 continue
122
123 except gmPG2.dbapi.OperationalError, e:
124 _log.error(u"login attempt failed: %s", e)
125 msg = _(
126 "Unable to connect to database:\n\n"
127 "%s\n\n"
128 "Please retry another backend / user / password combination !\n"
129 ) % gmPG2.extract_msg_from_pg_exception(e)
130 msg = regex.sub(r'password=[^\s]+', u'password=%s' % gmTools.u_replacement_character, msg)
131 gmGuiHelpersWeb.gm_show_error (
132 msg,
133 _('Connecting to backend')
134 )
135 del e
136 continue
137
138 # connect was successful
139 gmPG2.set_default_login(login = login)
140 #gmPG2.set_default_client_encoding(encoding = dlg.panel.backend_profile.encoding)
141
142 # compatible = gmPG2.database_schema_compatible(version = expected_version)
143 # if compatible or not require_version:
144 #dlg.panel.save_state()
145 # continue
146
147 # if not compatible:
148 # connected_db_version = gmPG2.get_schema_version()
149 # msg = msg_generic % (
150 # client_version,
151 # connected_db_version,
152 # expected_version,
153 # gmTools.coalesce(login.host, '<localhost>'),
154 # login.database,
155 # login.user
156 # )
157
158 # if require_version:
159 # gmGuiHelpersWeb.gm_show_error(msg + msg_fail, _('Verifying database version'))
160 # pass
161 #gmGuiHelpersWeb.gm_show_info(msg + msg_override, _('Verifying database version'))
162
163 # # FIXME: make configurable
164 # max_skew = 1 # minutes
165 # if _cfg.get(option = 'debug'):
166 # max_skew = 10
167 # if not gmPG2.sanity_check_time_skew(tolerance = (max_skew * 60)):
168 # if _cfg.get(option = 'debug'):
169 # gmGuiHelpersWeb.gm_show_warning(msg_time_skew_warn % max_skew, _('Verifying database settings'))
170 # else:
171 # gmGuiHelpersWeb.gm_show_error(msg_time_skew_fail % max_skew, _('Verifying database settings'))
172 # continue
173
174 # sanity_level, message = gmPG2.sanity_check_database_settings()
175 # if sanity_level != 0:
176 # gmGuiHelpersWeb.gm_show_error((msg_insanity % message), _('Verifying database settings'))
177 # if sanity_level == 2:
178 # continue
179
180 # gmExceptionHandlingWidgets.set_is_public_database(login.public_db)
181 # gmExceptionHandlingWidgets.set_helpdesk(login.helpdesk)
182
183 listener = gmBackendListener.gmBackendListener(conn = conn)
184 break
185
186 #dlg.Destroy()
187
188 return connected
189
190 #----------------------------------------------------------------------------
191 #internal helper functions
192 #----------------------------------------------------
193 -def __get_backend_profiles():
194 """Get server profiles from the configuration files.
195
196 1) from system-wide file
197 2) from user file
198
199 Profiles in the user file which have the same name
200 as a profile in the system file will override the
201 system file.
202 """
203 # find active profiles
204 src_order = [
205 (u'explicit', u'extend'),
206 (u'system', u'extend'),
207 (u'user', u'extend'),
208 (u'workbase', u'extend')
209 ]
210
211 profile_names = gmTools.coalesce (
212 _cfg.get(group = u'backend', option = u'profiles', source_order = src_order),
213 []
214 )
215
216 # find data for active profiles
217 src_order = [
218 (u'explicit', u'return'),
219 (u'workbase', u'return'),
220 (u'user', u'return'),
221 (u'system', u'return')
222 ]
223
224 profiles = {}
225
226 for profile_name in profile_names:
227 # FIXME: once the profile has been found always use the corresponding source !
228 # FIXME: maybe not or else we cannot override parts of the profile
229 profile = cBackendProfile()
230 profile_section = 'profile %s' % profile_name
231
232 profile.name = profile_name
233 profile.host = gmTools.coalesce(_cfg.get(profile_section, u'host', src_order), u'').strip()
234 port = gmTools.coalesce(_cfg.get(profile_section, u'port', src_order), 5432)
235 try:
236 profile.port = int(port)
237 if profile.port < 1024:
238 raise ValueError('refusing to use priviledged port (< 1024)')
239 except ValueError:
240 _log.warning('invalid port definition: [%s], skipping profile [%s]', port, profile_name)
241 continue
242 profile.database = gmTools.coalesce(_cfg.get(profile_section, u'database', src_order), u'').strip()
243 if profile.database == u'':
244 _log.warning('database name not specified, skipping profile [%s]', profile_name)
245 continue
246 profile.encoding = gmTools.coalesce(_cfg.get(profile_section, u'encoding', src_order), u'UTF8')
247 profile.public_db = bool(_cfg.get(profile_section, u'public/open access', src_order))
248 profile.helpdesk = _cfg.get(profile_section, u'help desk', src_order)
249
250 label = u'%s (%s@%s)' % (profile_name, profile.database, profile.host)
251 profiles[label] = profile
252
253 # sort out profiles with incompatible database versions if not --debug
254 # NOTE: this essentially hardcodes the database name in production ...
255 if not (_cfg.get(option = 'debug') or current_db_name.endswith('_devel')):
256 profiles2remove = []
257 for label in profiles:
258 if profiles[label].database != current_db_name:
259 profiles2remove.append(label)
260 for label in profiles2remove:
261 del profiles[label]
262
263 if len(profiles) == 0:
264 host = u'salaam.homeunix.com'
265 label = u'public GNUmed database (%s@%s)' % (current_db_name, host)
266 profiles[label] = cBackendProfile()
267 profiles[label].name = label
268 profiles[label].host = host
269 profiles[label].port = 5432
270 profiles[label].database = current_db_name
271 profiles[label].encoding = u'UTF8'
272 profiles[label].public_db = True
273 profiles[label].helpdesk = u'http://wiki.gnumed.de'
274
275 return profiles
276
277 # ------------------------------------------------------------
278 -def GetLoginInfo(username=None, password=None, backend=None ):
279
280 # username is provided through the web interface
281 # password is provided
282 # we need the profile
283
284 """convenience function for compatibility with gmLoginInfo.LoginInfo"""
285 #if not self.cancelled:
286 # FIXME: do not assume conf file is latin1 !
287 #profile = self.__backend_profiles[self._CBOX_profile.GetValue().encode('latin1').strip()]
288 #self.__backend_profiles = self.__get_backend_profiles()
289 __backend_profiles = __get_backend_profiles()
290 profile = __backend_profiles[backend.encode('utf8').strip()]
291
292 _log.debug(u'backend profile "%s" selected', profile.name)
293 _log.debug(u' details: <%s> on %s@%s:%s (%s, %s)',
294 username,
295 profile.database,
296 profile.host,
297 profile.port,
298 profile.encoding,
299 gmTools.bool2subst(profile.public_db, u'public', u'private')
300 )
301 #_log.debug(u' helpdesk: "%s"', profile.helpdesk)
302 login = gmLoginInfo.LoginInfo (
303 user = username,
304 password = password,
305 host = profile.host,
306 database = profile.database,
307 port = profile.port
308 )
309 #login.public_db = profile.public_db
310 #login.helpdesk = profile.helpdesk
311 return login
312
313 #----------------------------------------------
314 -def _signal_debugging_monitor(*args, **kwargs):
315 try:
316 kwargs['originated_in_database']
317 print '==> got notification from database "%s":' % kwargs['signal']
318 except KeyError:
319 print '==> received signal from client: "%s"' % kwargs['signal']
320
321 del kwargs['signal']
322 for key in kwargs.keys():
323 print ' [%s]: %s' % (key, kwargs[key])
324
328
332 print "before_handler jsonrpc"
333 # note: wheter req.body is a string or file depends on the content-type!
334 req = cherrypy.request
335 try:
336 size = int(req.headers["Content-Length"])
337 except:
338 size = 1
339 try:
340 json_string = req.body.read()
341 print "json_string [%s]" % json_string
342 obj = loads(json_string)
343 myparams = {}
344 for key, val in obj.items():
345 mykey = str(key)
346 myparams[mykey] = val
347 req.params = myparams
348 except:
349 pass
350 cherrypy.tools.jsonrpchdl = cherrypy.Tool('before_handler',jsonrpchdl)
351
352 PYJSDIR = sys._getframe().f_code.co_filename
353 PYJSDIR = os.path.split(os.path.dirname(PYJSDIR))[0]
354 PYJSDIR = os.path.join(PYJSDIR, 'pyjamas')
355
356 DEFAULT_BACKEND = "GNUmed database on this machine (Linux/Mac) (gnumed_v15@)"
359
360 @cherrypy.expose
362 fname = os.path.join(PYJSDIR,args[0])
363 print "try to return contents of file %s" % fname
364 f = file(fname)
365 s = f.read()
366 return s
367
368 @cherrypy.expose
369 @cherrypy.tools.jsonrpchdl()
371 print "echo service"
372 print args
373 print kwargs
374 method = kwargs['method']
375 f = getattr(self,method)
376 res = f(*kwargs['params'])
377 return dumps({'id':kwargs['id'],'result':res,'error':None})
386
389
391 res = []
392 for item in gmDocuments.get_document_types():
393 res.append(str(item))
394 return res
395
397 msg = 'schema version is:' + self.get_schema_version() +'\n\n'
398 msg2 =''
399 for item in gmDocuments.get_document_types():
400 msg2 = msg2 +'\n' + str(item)
401 msg = msg + msg2
402 return "<pre>%s</pre>" %msg
403
405 if backend is None:
406 backend = DEFAULT_BACKEND
407 login_info = GetLoginInfo(username, password, backend)
408 override = _cfg.get(option = '--override-schema-check',
409 source_order = [('cli', 'return')])
410 cb = _cfg.get(option = 'client_branch')
411 expected_version = gmPG2.map_client_branch2required_db_version[cb]
412 connected = connect_to_database (
413 login_info,
414 expected_version = expected_version,
415 require_version = not override
416 )
417 return connected
418
420 login_info = GetLoginInfo(username, password, backend)
421 override = _cfg.get(option = '--override-schema-check',
422 source_order = [('cli', 'return')])
423 cb = _cfg.get(option = 'client_branch')
424 expected_version = gmPG2.map_client_branch2required_db_version[cb]
425 connected = connect_to_database (
426 login_info,
427 expected_version = expected_version,
428 require_version = not override
429 )
430 if connected:
431 msg = self.doSomething()
432 return msg
433 else:
434 return 'something went wrong'
435
436 doLogin.exposed = True
437
438 # ------------------------------------------------------------
440 # backend is hardcoded for now, make it use drop down list later
441 # building the html out of the Cheetah Template
442 t = Template( file="CherryPy/templates/index.tmpl"
443 # a dictionnary containing values that is going to be inserted in the Template
444 , searchList = {
445 "title" : "Welcome to GNUmed - Login"
446 , "cssFiles" : ["css/ext-all.css", "css/xtheme-gray.css"]
447 , "jsFiles" : ["ext/ext-base.js", "ext/ext-core.js"]
448 , "backend" : "GNUmed database on this machine (Linux/Mac) (gnumed_v15@)"
449 }
450 )
451 return str( t ) # returning a string representation of the Template. CherryPy will only let you return strings with an exposed function
452
453
454 #return """
455 #<form action="doLogin" method="post">
456 # <p>Backend</p>
457 # <input type="text" name="backend" value="GNUmed database on this machine (Linux/Mac) (gnumed_v15@)"
458 # size="15" maxlength="40"/>
459 # <p>Username</p>
460 # <input type="text" name="username" value=""
461 # size="15" maxlength="40"/>
462 # <p>Password</p>
463 # <input type="password" name="password" value=""
464 # size="10" maxlength="40"/>
465 # <p><input type="submit" value="Login"/></p>
466 # <p><input type="reset" value="Clear"/></p>
467 #</form>
468 #"""
469 index.exposed = True
470
483
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Nov 29 04:05:58 2010 | http://epydoc.sourceforge.net |