| 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 v2 or later (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, 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_v%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'publicdb.gnumed.de'
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 # username is provided through the web interface
280 # password is provided
281 # we need the profile
282
283 """convenience function for compatibility with gmLoginInfo.LoginInfo"""
284 #if not self.cancelled:
285 # FIXME: do not assume conf file is latin1 !
286 #profile = self.__backend_profiles[self._CBOX_profile.GetValue().encode('latin1').strip()]
287 #self.__backend_profiles = self.__get_backend_profiles()
288 __backend_profiles = __get_backend_profiles()
289 profile = __backend_profiles[backend.encode('utf8').strip()]
290
291 _log.debug(u'backend profile "%s" selected', profile.name)
292 _log.debug(u' details: <%s> on %s@%s:%s (%s, %s)',
293 username,
294 profile.database,
295 profile.host,
296 profile.port,
297 profile.encoding,
298 gmTools.bool2subst(profile.public_db, u'public', u'private')
299 )
300 #_log.debug(u' helpdesk: "%s"', profile.helpdesk)
301 login = gmLoginInfo.LoginInfo (
302 user = username,
303 password = password,
304 host = profile.host,
305 database = profile.database,
306 port = profile.port
307 )
308 #login.public_db = profile.public_db
309 #login.helpdesk = profile.helpdesk
310 return login
311
312 #----------------------------------------------
313 -def _signal_debugging_monitor(*args, **kwargs):
314 try:
315 kwargs['originated_in_database']
316 print '==> got notification from database "%s":' % kwargs['signal']
317 except KeyError:
318 print '==> received signal from client: "%s"' % kwargs['signal']
319
320 del kwargs['signal']
321 for key in kwargs.keys():
322 print ' [%s]: %s' % (key, kwargs[key])
323
327
331 print "before_handler jsonrpc"
332 # note: wheter req.body is a string or file depends on the content-type!
333 req = cherrypy.request
334 try:
335 size = int(req.headers["Content-Length"])
336 except:
337 size = 1
338 try:
339 json_string = req.body.read()
340 print "json_string [%s]" % json_string
341 obj = loads(json_string)
342 myparams = {}
343 for key, val in obj.items():
344 mykey = str(key)
345 myparams[mykey] = val
346 req.params = myparams
347 except:
348 pass
349 cherrypy.tools.jsonrpchdl = cherrypy.Tool('before_handler',jsonrpchdl)
350
351 PYJSDIR = sys._getframe().f_code.co_filename
352 PYJSDIR = os.path.split(os.path.dirname(PYJSDIR))[0]
353 PYJSDIR = os.path.join(PYJSDIR, 'pyjamas')
354
355 DEFAULT_BACKEND = "GNUmed database on this machine (Linux/Mac) (gnumed_v17@)"
358
359 @cherrypy.expose
361 fname = os.path.join(PYJSDIR,args[0])
362 print "try to return contents of file %s" % fname
363 f = file(fname)
364 s = f.read()
365 return s
366
367 @cherrypy.expose
368 @cherrypy.tools.jsonrpchdl()
370 print "echo service"
371 print args
372 print kwargs
373 method = kwargs['method']
374 f = getattr(self,method)
375 res = f(*kwargs['params'])
376 return dumps({'id':kwargs['id'],'result':res,'error':None})
385
388
390 res = []
391 for item in gmDocuments.get_document_types():
392 res.append(str(item))
393 return res
394
396 msg = 'schema version is:' + self.get_schema_version() +'\n\n'
397 msg2 =''
398 for item in gmDocuments.get_document_types():
399 msg2 = msg2 +'\n' + str(item)
400 msg = msg + msg2
401 return "<pre>%s</pre>" %msg
402
404 if backend is None:
405 backend = DEFAULT_BACKEND
406 login_info = GetLoginInfo(username, password, backend)
407 override = _cfg.get(option = '--override-schema-check',
408 source_order = [('cli', 'return')])
409 cb = _cfg.get(option = 'client_branch')
410 expected_version = gmPG2.map_client_branch2required_db_version[cb]
411 connected = connect_to_database (
412 login_info,
413 expected_version = expected_version,
414 require_version = not override
415 )
416 return connected
417
419 login_info = GetLoginInfo(username, password, backend)
420 override = _cfg.get(option = '--override-schema-check',
421 source_order = [('cli', 'return')])
422 cb = _cfg.get(option = 'client_branch')
423 expected_version = gmPG2.map_client_branch2required_db_version[cb]
424 connected = connect_to_database (
425 login_info,
426 expected_version = expected_version,
427 require_version = not override
428 )
429 if connected:
430 msg = self.doSomething()
431 return msg
432 else:
433 return 'something went wrong'
434
435 doLogin.exposed = True
436
437 # ------------------------------------------------------------
439 # backend is hardcoded for now, make it use drop down list later
440 # building the html out of the Cheetah Template
441 t = Template( file="CherryPy/templates/index.tmpl"
442 # a dictionnary containing values that is going to be inserted in the Template
443 , searchList = {
444 "title" : "Welcome to GNUmed - Login"
445 , "cssFiles" : ["css/ext-all.css", "css/xtheme-gray.css"]
446 , "jsFiles" : ["ext/ext-base.js", "ext/ext-core.js"]
447 , "backend" : "GNUmed database on this machine (Linux/Mac) (gnumed_v17@)"
448 }
449 )
450 return str( t ) # returning a string representation of the Template. CherryPy will only let you return strings with an exposed function
451
452
453 #return """
454 #<form action="doLogin" method="post">
455 # <p>Backend</p>
456 # <input type="text" name="backend" value="GNUmed database on this machine (Linux/Mac) (gnumed_v17@)"
457 # size="15" maxlength="40"/>
458 # <p>Username</p>
459 # <input type="text" name="username" value=""
460 # size="15" maxlength="40"/>
461 # <p>Password</p>
462 # <input type="password" name="password" value=""
463 # size="10" maxlength="40"/>
464 # <p><input type="submit" value="Login"/></p>
465 # <p><input type="reset" value="Clear"/></p>
466 #</form>
467 #"""
468 index.exposed = True
469
482
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 25 03:58:57 2012 | http://epydoc.sourceforge.net |