def init_cgiscript_possibly_with_cert(print_header=True, content_type='text/html'): """Prepare for CGI script with optional client certificate. Only used from some of the cgi scripts still on the legacy-form like requestnewjob and put. I.e. scripts where certs are not required due to use of sessionid. """ # Always rely on os.environ here since only called from cgi scripts environ = os.environ if print_header: cgiscript_header(content_type=content_type) configuration = get_configuration_object() logger = configuration.logger out = CGIOutput(logger) # get DN of user currently logged in client_id = extract_client_id(configuration, environ) if not client_id: logger.debug('(No client ID available in SSL session)') logger.info('script: %s cert: %s' % (requested_page(), client_id)) return (logger, configuration, client_id, out)
def application(environ, start_response): """MiG app called automatically by wsgi""" # TODO: verify security of this environment exposure # pass environment on to sub handlers os.environ = environ # TODO: we should avoid print calls completely in backends # make sure print calls do not interfere with wsgi sys.stdout = sys.stderr configuration = get_configuration_object() # get and log ID of user currently logged in # We can't import helper before environ is ready because it indirectly # tries to use pre-mangled environ for conf loading from shared.httpsclient import extract_client_id client_id = extract_client_id(configuration, environ) fieldstorage = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ) user_arguments_dict = fieldstorage_to_dict(fieldstorage) # default to html output_format = 'html' if user_arguments_dict.has_key('output_format'): output_format = user_arguments_dict['output_format'][0] try: if not configuration.site_enable_wsgi: raise Exception("WSGI interface not enabled for this grid") # Environment contains python script _somewhere_ , try in turn # and fall back to dashboard if all fails script_path = requested_page(environ, 'dashboard.py') backend = os.path.basename(script_path).replace('.py' , '') module_path = 'shared.functionality.%s' % backend (output_objs, ret_val) = stub(module_path, configuration, client_id, user_arguments_dict, environ) status = '200 OK' except Exception, exc: status = '500 ERROR' (output_objs, ret_val) = ([{'object_type': 'title', 'text' : 'Unsupported Interface'}, {'object_type': 'error_text', 'text' : str(exc)}, # Enable next two lines only for debugging # {'object_type': 'text', 'text': # str(environ)} {'object_type': 'link', 'text': 'Go to default interface', 'destination': '/index.html'}, ], returnvalues.SYSTEM_ERROR)
def init_cgi_script(environ, delayed_input=None): """Shared init""" configuration = get_configuration_object() logger = configuration.logger # get and log ID of user currently logged in client_id = extract_client_id(configuration, environ) logger.info('script: %s cert: %s' % (requested_page(), client_id)) if not delayed_input: fieldstorage = cgi.FieldStorage() user_arguments_dict = fieldstorage_to_dict(fieldstorage) else: user_arguments_dict = {'__DELAYED_INPUT__': delayed_input} return (configuration, logger, client_id, user_arguments_dict)
def initialize_main_variables(client_id, op_title=True, op_header=True, op_menu=True): """Script initialization is identical for most scripts in shared/functionalty. This function should be called in most cases. """ configuration = get_configuration_object() logger = configuration.logger output_objects = [] start_entry = make_start_entry() output_objects.append(start_entry) op_name = os.path.basename(requested_page()).replace(".py", "") if op_title: title_object = make_title_entry("%s" % op_name, skipmenu=(not op_menu)) output_objects.append(title_object) if op_header: header_object = make_header_entry("%s" % op_name) output_objects.append(header_object) if client_id: # add the user-defined menu and widgets (if possible) title = find_entry(output_objects, "title") if title: settings = load_settings(client_id, configuration) if settings: base_menu = settings.get("SITE_BASE_MENU", "default") if not base_menu in configuration.site_base_menu: base_menu = "default" if base_menu == "simple" and configuration.site_simple_menu: title["base_menu"] = configuration.site_simple_menu elif base_menu == "advanced" and configuration.site_advanced_menu: title["base_menu"] = configuration.site_advanced_menu else: title["base_menu"] = configuration.site_default_menu user_menu = settings.get("SITE_USER_MENU", None) if configuration.site_user_menu and user_menu: title["user_menu"] = user_menu if settings.get("ENABLE_WIDGETS", True) and configuration.site_script_deps: user_widgets = load_widgets(client_id, configuration) if user_widgets: title["user_widgets"] = user_widgets return (configuration, logger, output_objects, op_name)
def validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects, require_user=True, filter_values=None, environ=None, ): """A wrapper used by most back end functionality - redirects to sign up if client_id is missing. """ logger = configuration.logger if environ is None: environ = os.environ creds_error = '' if not client_id: creds_error = "Invalid or missing user credentials" elif require_user and not is_user(client_id, configuration.mig_server_home): creds_error = "No such user (%s)" % client_id if creds_error and not requested_page().endswith('logout.py'): output_objects.append({'object_type': 'error_text', 'text' : creds_error }) # Redirect to sign-up cert page trying to guess relevant choices signup_url = os.path.join(configuration.migserver_https_sid_url, 'cgi-sid', 'signup.py') signup_query = '' if not client_id: output_objects.append( {'object_type': 'text', 'text': '''Apparently you do not already have access to %s, but you can sign up:''' % configuration.short_title }) output_objects.append({'object_type': 'link', 'text': signup_url, 'destination': signup_url + signup_query}) output_objects.append( {'object_type': 'text', 'text': '''If you already signed up and received a user certificate you probably just need to import it in your browser.'''}) else: output_objects.append( {'object_type': 'text', 'text': '''Apparently you already have suitable credentials and just need to sign up for a local %s account on:''' % \ configuration.short_title}) if extract_client_cert(configuration, environ) is None: # Force logout/expire session cookie here to support signup identity = extract_client_openid(configuration, environ, lookup_dn=False) if identity: logger.info("expire openid user %s" % identity) (success, _) = expire_oid_sessions(configuration, identity) else: logger.info("no openid user logged in") output_objects.append({'object_type': 'link', 'text': signup_url, 'destination': signup_url + signup_query}) return (False, output_objects) (status, retval) = validate_input(user_arguments_dict, defaults, output_objects, allow_rejects, filter_values) return (status, retval)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) output_objects.append({ 'object_type': 'header', 'text': 'Grid Usage Statistics' }) defaults = signature()[1] (validate_status, accepted) = validate_input(user_arguments_dict, defaults, output_objects, allow_rejects=False) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Usage Statistics' # read view options group_in_time = accepted['group_in_time'][-1] # day, week, month time_start = accepted['time_start'][-1] time_end = accepted['time_end'][-1] display = accepted['display'][-1] # machine, user, vgrid, summary if not configuration.site_enable_griddk: output_objects.append({ 'object_type': 'text', 'text': '''Grid.dk features are disabled on this site. Please contact the site admins %s if you think they should be enabled. ''' % configuration.admin_email }) return (output_objects, returnvalues.OK) # check arguments against configured lists of valid inputs: reject = False # make sure: grouping in ['user','machine', 'vgrid'] if not display in displays: output_objects.append({ 'object_type': 'error_text', 'text': 'invalid display grouping specified: %s' % display }) display = displays[0] reject = True # make sure: group_in_time in ['all', 'month', 'day', 'week'] if not group_in_time in time_groups: output_objects.append({ 'object_type': 'error_text', 'text': 'invalid time grouping specified: %s' % group_in_time }) group_in_time = time_groups[0] reject = True # make sure: start and end match "20[0-9]{2}-[01][0-9]" if not re.match('20\d\d-[01]\d', time_start): output_objects.append({ 'object_type': 'error_text', 'text': 'invalid start time specified: %s' % time_start }) time_start = '2009-09' reject = True if not re.match('20\d\d-[01]\d', time_end): output_objects.append({ 'object_type': 'error_text', 'text': 'invalid end time specified: %s' % time_end }) time_end = time.strftime('%Y-%m') reject = True # always include a form to re-display with different values: updateform = ' <form action="%s" >' % \ os.path.basename(requested_page()) updateform += ''' <table class="runtimeenventry"> <thead> <tr> <th>Grouping by time</th> <th>Start (YYYY-MM)</th> <th>End (YYYY-MM)</th> <th>Category</th> <th><!-- dummy for update button --></th> </tr> </thead> <tbody> <tr> <td> <select name="group_in_time"> ''' # updateform += \ # ''.join([ '\n<option value="' + t + '">' + t.title() + '</option>' # for t in time_groups ]) for t in time_groups: updateform += '<option ' if group_in_time == t: updateform += 'selected ' updateform += 'value="' + t + '">' + t.title() + '</option>\n' updateform += ''' </select> </td> <td><input type="text" name="time_start" value="%s"></td> <td><input type="text" name="time_end" value="%s"></td> ''' % (time_start, time_end) + ''' <td><select name="display"> ''' # updateform += \ # ''.join([ '\n<option value="' + d + '">' + d.title() + '</option>' # for d in displays ]) for d in displays: updateform += '<option ' if display == d: updateform += 'selected ' updateform += 'value="' + d + '">' + d.title() + '</option>\n' updateform += ''' </select> </td> <td><input type="submit" value="Update View"></td> </tr> </tbody> </table> </form> <hr> ''' output_objects.append({'object_type': 'html_form', 'text': updateform}) if reject: output_objects.append({ 'object_type': 'text', 'text': 'Please check your view parameters.' }) return (output_objects, returnvalues.CLIENT_ERROR) # else: all parameters OK, go: # determine the view and group level to use: # we use couchdb views with name convention <group-in-time>-<display> view = group_in_time + '-' + display # default group-level (max. 2 view components relevant) group_level = 2 # could handle summary display specially, by setting group_level = 1 # and using either <time>-machine or <time>-user view # if display == 'summary': # display = 'user' # group_level = 1 # construct start and end key. # machine and user cannot be filtered from the user, only # time can be specified, and as YYYY-MM only. # for view per week, we have to convert it to a week number # python starts by week 0, whereas javascript starts by week 1 if group_in_time == 'week': t = time.strptime(time_start + '-07', "%Y-%m-%d") time_start = time.strftime("%Y,week%U", t) start_key = '["' + time_start.replace("-", " ") + '",null]' # 2nd component: user or machine # TODO allow only own user ID and only machines owned??? # drawback: cannot restrict user/machine when requesting more than one time period # To restrict user/machine, views which have this as the first key part # have to be used. In which case only one user can be selected # to keep the time period selection valid. if group_in_time == 'week': # last week of the month = first week 1 month later # we better compute this instead of tweaking the input t = time.mktime(time.strptime(time_end + '-28', "%Y-%m-%d")) t += 7 * 24 * 3600 end_key = '["' + \ time.strftime("%Y,week%U", time.localtime(t)) + '",{}]' else: end_key = '["' + time_end.replace("-", " ") + ' 32' + '",{}]' # append last day, so inclusive end # 1. get json data from couchdb using the view # group=true, group_level as calculated, # start and end key as constructed # couchdb URL, default http://localhost:5984/ # TODO use configuration.usagedb database = 'http://localhost:5984/usagerecords' [check, db_url, db_name] = database.rsplit('/', 2) if check and check != 'http:/': logger.debug('bad URL %s' % database) db_url = 'none' db_name = 'none' # views are organised in files per "timegrouping", # and contain views with names <category>-<timegrouping> query = '/'.join(['', db_name, '_design', group_in_time, '_view', view]) query += '?' query += urllib.urlencode({ 'group': 'true', 'group_level': group_level, 'startkey': start_key, 'endkey': end_key, }) try: logger.debug("asking database at %s: %s" % (db_url, query)) # Never use proxies res = urllib.urlopen('http://' + db_url + query, proxies={}) jsonreply = res.read() res.close() except Exception, err: logger.error('Could not get data from database: %s' % err) output_objects.append({ 'object_type': 'error_text', 'text': 'Error accessing the database.' }) jsonreply = '{"rows":[]}'
def validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects, require_user=True, filter_values=None, environ=None, ): """A wrapper used by most back end functionality - redirects to sign up if client_id is missing. """ logger = configuration.logger if environ is None: environ = os.environ creds_error = '' if not client_id: creds_error = "Invalid or missing user credentials" elif require_user and not is_user(client_id, configuration.mig_server_home): creds_error = "No such user (%s)" % client_id if creds_error and not requested_page().endswith('logout.py'): output_objects.append({ 'object_type': 'error_text', 'text': creds_error }) if configuration.site_enable_gdp: main_url = configuration.migserver_http_url output_objects.append({ 'object_type': 'text', 'text': '''Apparently you do not have access to this page, please return to:''' }) output_objects.append({ 'object_type': 'link', 'text': main_url, 'destination': main_url }) else: # Redirect to sign-up cert page trying to guess relevant choices signup_url = os.path.join(configuration.migserver_https_sid_url, 'cgi-sid', 'signup.py') signup_query = '' if not client_id: output_objects.append({ 'object_type': 'text', 'text': '''Apparently you do not already have access to %s, but you can sign up:''' % configuration.short_title }) output_objects.append({'object_type': 'link', 'text': '%s sign up page' % \ configuration.short_title, 'destination': signup_url + signup_query}) output_objects.append({ 'object_type': 'text', 'text': '''If you already signed up and received a user certificate you probably just need to import it in your browser.''' }) else: output_objects.append({ 'object_type': 'text', 'text': '''Apparently you already have suitable credentials and just need to sign up for a local %s account on the''' % configuration.short_title }) base_url = extract_base_url(configuration, environ) if base_url == configuration.migserver_https_ext_cert_url and \ 'extcert' in configuration.site_login_methods: signup_query = '?show=extcert' elif base_url in (configuration.migserver_https_ext_oid_url, configuration.migserver_https_mig_oid_url): # Force logout/expire session cookie here to support signup (oid_db, identity) = extract_client_openid(configuration, environ, lookup_dn=False) if oid_db and identity: logger.info("openid expire user %s in %s" % (identity, oid_db)) (success, _) = expire_oid_sessions(configuration, oid_db, identity) if oid_db == auth_openid_ext_db and \ 'extoid' in configuration.site_signup_methods: signup_query = '?show=extoid' else: logger.error("unknown migoid client_id %s on %s" \ % (client_id, base_url)) else: logger.warning("unexpected client_id %s on %s" % \ (client_id, base_url)) output_objects.append({'object_type': 'link', 'text': '%s sign up page' % \ configuration.short_title, 'destination': signup_url + signup_query}) return (False, output_objects) (status, retval) = validate_input(user_arguments_dict, defaults, output_objects, allow_rejects, filter_values) return (status, retval)
def initialize_main_variables(client_id, op_title=True, op_header=True, op_menu=True): """Script initialization is identical for most scripts in shared/functionalty. This function should be called in most cases. """ configuration = get_configuration_object() logger = configuration.logger output_objects = [] start_entry = make_start_entry() output_objects.append(start_entry) op_name = os.path.splitext(os.path.basename(requested_page()))[0] if op_title: skipwidgets = not configuration.site_enable_widgets or not client_id skipuserstyle = not configuration.site_enable_styling or not client_id title_object = make_title_entry('%s' % op_name, skipmenu=(not op_menu), skipwidgets=skipwidgets, skipuserstyle=skipuserstyle, skipuserprofile=(not client_id)) # Make sure base_menu is always set for extract_menu # Typicall overriden based on client_id cases below title_object['base_menu'] = configuration.site_default_menu output_objects.append(title_object) if op_header: header_object = make_header_entry('%s' % op_name) output_objects.append(header_object) if client_id: # add the user-defined menu and widgets (if possible) title = find_entry(output_objects, 'title') if title: settings = load_settings(client_id, configuration) # NOTE: loaded settings may be False rather than dict here if not settings: settings = {} title['style'] = themed_styles(configuration, user_settings=settings) title['script'] = themed_scripts(configuration, user_settings=settings) if settings: title['user_settings'] = settings base_menu = settings.get('SITE_BASE_MENU', 'default') if not base_menu in configuration.site_base_menu: base_menu = 'default' if base_menu == 'simple' and configuration.site_simple_menu: title['base_menu'] = configuration.site_simple_menu elif base_menu == 'advanced' and \ configuration.site_advanced_menu: title['base_menu'] = configuration.site_advanced_menu else: title['base_menu'] = configuration.site_default_menu user_menu = settings.get('SITE_USER_MENU', None) if configuration.site_user_menu and user_menu: title['user_menu'] = user_menu if settings.get('ENABLE_WIDGETS', True) and \ configuration.site_script_deps: user_widgets = load_widgets(client_id, configuration) if user_widgets: title['user_widgets'] = user_widgets user_profile = load_profile(client_id, configuration) if user_profile: # These data are used for display in own profile view only profile_image_list = user_profile.get('PUBLIC_IMAGE', []) if profile_image_list: # TODO: copy profile image to /public/avatars/X and use it profile_image = os.path.join( configuration.site_user_redirect, profile_image_list[-1]) else: profile_image = '' user_profile['profile_image'] = profile_image else: user_profile = {} # Always set full name for use in personal user menu full_name = extract_field(client_id, 'full_name') user_profile['full_name'] = full_name title['user_profile'] = user_profile logger.debug('setting user profile: %s' % user_profile) else: # No user so we just enforce default site style and scripts title = find_entry(output_objects, 'title') if title: title['style'] = themed_styles(configuration) title['script'] = themed_scripts(configuration, logged_in=False) return (configuration, logger, output_objects, op_name)
def application(environ, start_response): """MiG app called automatically by wsgi""" # TODO: verify security of this environment exposure # pass environment on to sub handlers os.environ = environ # TODO: we should avoid print calls completely in backends # make sure print calls do not interfere with wsgi sys.stdout = sys.stderr configuration = get_configuration_object() _logger = configuration.logger # get and log ID of user currently logged in # We can't import helper before environ is ready because it indirectly # tries to use pre-mangled environ for conf loading from shared.httpsclient import extract_client_id client_id = extract_client_id(configuration, environ) fieldstorage = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ) user_arguments_dict = fieldstorage_to_dict(fieldstorage) # default to html output_format = 'html' if user_arguments_dict.has_key('output_format'): output_format = user_arguments_dict['output_format'][0] backend = "UNKNOWN" output_objs = [] try: if not configuration.site_enable_wsgi: _logger.error("WSGI interface is disabled in configuration") raise Exception("WSGI interface not enabled for this site") # Environment contains python script _somewhere_ , try in turn # and fall back to dashboard if all fails script_path = requested_page(environ, configuration.site_landing_page) script_name = os.path.basename(script_path) backend = os.path.splitext(script_name)[0] module_path = 'shared.functionality.%s' % backend (allow, msg) = allow_script(configuration, script_name, client_id) if allow: (output_objs, ret_val) = stub(configuration, client_id, module_path, backend, user_arguments_dict, environ) else: (output_objs, ret_val) = reject_main(client_id, user_arguments_dict) status = '200 OK' except Exception, exc: _logger.error("handling of WSGI request for %s from %s failed: %s" % (backend, client_id, exc)) status = '500 ERROR' crash_helper(configuration, backend, output_objs) output_objs.append({ 'object_type': 'link', 'text': 'Go to default interface', 'destination': configuration.site_landing_page }) ret_val = returnvalues.SYSTEM_ERROR
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) output_objects.append({'object_type': 'header', 'text': 'Grid Usage Statistics'}) defaults = signature()[1] (validate_status, accepted) = validate_input(user_arguments_dict, defaults, output_objects, allow_rejects=False) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Usage Statistics' # read view options group_in_time = accepted['group_in_time'][-1] # day, week, month time_start = accepted['time_start'][-1] time_end = accepted['time_end'][-1] display = accepted['display'][-1] # machine, user, vgrid, summary if not configuration.site_enable_griddk: output_objects.append({'object_type': 'text', 'text': '''Grid.dk features are disabled on this site. Please contact the Grid admins %s if you think they should be enabled. ''' % configuration.admin_email}) return (output_objects, returnvalues.OK) # check arguments against configured lists of valid inputs: reject = False # make sure: grouping in ['user','machine', 'vgrid'] if not display in displays: output_objects.append({'object_type': 'error_text', 'text' : 'invalid display grouping specified: %s' % display }) display = displays[0] reject = True # make sure: group_in_time in ['all', 'month', 'day', 'week'] if not group_in_time in time_groups: output_objects.append({'object_type': 'error_text', 'text' : 'invalid time grouping specified: %s' % group_in_time }) group_in_time = time_groups[0] reject = True # make sure: start and end match "20[0-9]{2}-[01][0-9]" if not re.match('20\d\d-[01]\d', time_start): output_objects.append({'object_type': 'error_text', 'text' : 'invalid start time specified: %s' % time_start }) time_start = '2009-09' reject = True if not re.match('20\d\d-[01]\d', time_end): output_objects.append({'object_type': 'error_text', 'text' : 'invalid end time specified: %s' % time_end }) time_end = time.strftime('%Y-%m') reject = True # always include a form to re-display with different values: updateform = ' <form action="%s" >' % \ os.path.basename(requested_page()) updateform +=''' <table class="runtimeenventry"> <thead> <tr> <th>Grouping by time</th> <th>Start (YYYY-MM)</th> <th>End (YYYY-MM)</th> <th>Category</th> <th><!-- dummy for update button --></th> </tr> </thead> <tbody> <tr> <td> <select name="group_in_time"> ''' # updateform += \ # ''.join([ '\n<option value="' + t + '">' + t.title() + '</option>' # for t in time_groups ]) for t in time_groups: updateform +='<option ' if group_in_time == t: updateform += 'selected ' updateform += 'value="' + t + '">' + t.title() + '</option>\n' updateform +=''' </select> </td> <td><input type="text" name="time_start" value="%s"></td> <td><input type="text" name="time_end" value="%s"></td> ''' % (time_start, time_end) + ''' <td><select name="display"> ''' # updateform += \ # ''.join([ '\n<option value="' + d + '">' + d.title() + '</option>' # for d in displays ]) for d in displays: updateform +='<option ' if display == d: updateform += 'selected ' updateform += 'value="' + d + '">' + d.title() + '</option>\n' updateform +=''' </select> </td> <td><input type="submit" value="Update View"></td> </tr> </tbody> </table> </form> <hr> ''' output_objects.append({'object_type': 'html_form', 'text': updateform}) if reject: output_objects.append({'object_type': 'text', 'text' : 'Please check your view parameters.'}) return(output_objects, returnvalues.CLIENT_ERROR) # else: all parameters OK, go: # determine the view and group level to use: # we use couchdb views with name convention <group-in-time>-<display> view = group_in_time + '-' + display # default group-level (max. 2 view components relevant) group_level = 2 # could handle summary display specially, by setting group_level = 1 # and using either <time>-machine or <time>-user view # if display == 'summary': # display = 'user' # group_level = 1 # construct start and end key. # machine and user cannot be filtered from the user, only # time can be specified, and as YYYY-MM only. # for view per week, we have to convert it to a week number # python starts by week 0, whereas javascript starts by week 1 if group_in_time == 'week': t = time.strptime(time_start + '-07',"%Y-%m-%d") time_start = time.strftime("%Y,week%U",t) start_key = '["'+ time_start.replace("-"," ") + '",null]' # 2nd component: user or machine # TODO allow only own user ID and only machines owned??? # drawback: cannot restrict user/machine when requesting more than one time period # To restrict user/machine, views which have this as the first key part # have to be used. In which case only one user can be selected # to keep the time period selection valid. if group_in_time == 'week': # last week of the month = first week 1 month later # we better compute this instead of tweaking the input t = time.mktime(time.strptime(time_end + '-28',"%Y-%m-%d")) t += 7*24*3600 end_key = '["'+ time.strftime("%Y,week%U",time.localtime(t)) + '",{}]' else: end_key = '["'+ time_end.replace("-"," ") + ' 32' + '",{}]' # append last day, so inclusive end # 1. get json data from couchdb using the view # group=true, group_level as calculated, # start and end key as constructed # couchdb URL, default http://localhost:5984/ # TODO use configuration.usagedb database ='http://localhost:5984/usagerecords' [check,db_url,db_name] = database.rsplit('/',2) if check and check != 'http:/': logger.debug('bad URL %s' % database) db_url = 'none' db_name = 'none' # views are organised in files per "timegrouping", # and contain views with names <category>-<timegrouping> query = '/'.join(['',db_name,'_design' ,group_in_time,'_view',view]) query += '?' query += urllib.urlencode({'group' : 'true', 'group_level': group_level, 'startkey' : start_key, 'endkey' : end_key, }) try: logger.debug("asking database at %s: %s" % (db_url,query)) res = urllib.urlopen('http://' + db_url + query) jsonreply = res.read() res.close() except Exception, err: logger.error('Could not get data from database: %s' % err) output_objects.append({'object_type': 'error_text', 'text' : 'Error accessing the database.' }) jsonreply = '{"rows":[]}'