def _generate_report_items(report): ''' calculate stats and generate data for graphs ahead of report rendering return a dict of the calculations and objects ''' # get all relevant data for the project conditions = { 'project': report.project , 'unique': True , 'visible': True } entries = Entry.objects(**conditions) items = [] for component in report.components: # determine if component is a stat or graph component_type = None try: component.statistic_type component_type = 'statistic' except: pass try: component.xaxis component_type = 'graph' except: pass if component_type == 'statistic': # find the statistic statistics = Statistic.objects(name=component.name , project=report.project) if not statistics: flash('Statistic "%s" not found' % component.name, 'error') return redirect(url_for('reports' , org_label=report.project.organization.label , project_label=report.project.label , report_label=report.label)) statistic = statistics[0] # apply relevant filters and then computes the statistic if statistic.pivot: # returns dict keyed by the statistic's pivot_values result = utilities.compute_pivot_statistic(statistic , entries) else: # returns a single result result = utilities.compute_statistic(statistic, entries) # save values for rendering items.append({ 'type': 'statistic' , 'name': component.name , 'instance': statistic , 'result': result }) elif component_type == 'graph': # find the graph graphs = Graph.objects(name=component.name , project=report.project) if not graphs: flash('Graph "%s" not found' % component.name, 'error') return redirect(url_for('reports' , org_label=report.project.organization.label , project_label=report.project.label , report_label=report.label)) graph = graphs[0] if graph.graph_type == 'line': if graph.xaxis and graph.yaxis: data, count = utilities.generate_line_graph_data(graph) else: data = [] flash('Define an x-axis and y-axis for plotting' , 'error') elif graph.graph_type == 'pie': if graph.pie_header: data, count = utilities.generate_pie_chart_data(graph) else: flash('define a column to create this pie chart' , 'warning') # save values for rendering items.append({ 'type': 'graph' , 'name': component.name , 'instance': graph , 'data': data }) return items
def statistics(org_label, project_label, statistic_label): ''' creating statistics for various purposes /organizations/aquaya/projects/water-quality/statistics : viewing a list of this project's statistics /organizations/aquaya/projects/water-quality/statistics?create=true : create a new statistic config, immediately redirect to editing /organizations/aquaya/projects/water-quality/statistics/mean-pH : view a statistic /organizations/aquaya/projects/water-quality/statistics/mean-pH?edit=true : edit a statistic; accepts GET or POST ''' user = User.objects(email=session['email'])[0] orgs = Organization.objects(label=org_label) if not orgs: flash('Organization "%s" not found, sorry!' % org_label, 'warning') return redirect(url_for('organizations')) org = orgs[0] # permission-check if org not in user.organizations and not user.admin_rights: app.logger.error('%s tried to view a project but was \ denied for want of admin rights' % session['email']) abort(404) # find the project projects = Project.objects(label=project_label, organization=org) if not projects: flash('Project "%s" not found, sorry!' % project_label, 'warning') return redirect(url_for('organizations', org_label=org.label)) project = projects[0] if request.method == 'POST': # we have a statistic_label statistics = Statistic.objects(label=statistic_label, project=project) if not statistics: abort(404) statistic = statistics[0] form_type = request.form.get('form_type', '') if form_type == 'info': if statistic.name != request.form.get('name', ''): name = request.form.get('name', '') statistic.name = name statistics = Statistic.objects(project=project).only('label') labels = [s.label for s in statistics] statistic.label = utilities.generate_label(name, labels) statistic.description = request.form.get('description', '') statistic.statistic_type = request.form.get('statistic_type', '') # header is of the form 'header_id__4abcd0012' header = request.form.get('header', '') header_id = header.split('header_id__')[1] header = Header.objects(id=header_id)[0] statistic.header = header elif form_type == 'create-table': compute_multiple = request.form.get('compute_multiple', '') if compute_multiple == 'true': statistic.pivot = True else: statistic.pivot = False header_id = request.form.get('compute_multiple_header', '') headers = Header.objects(project=project, id=header_id) if not headers: abort(404) statistic.pivot_header = headers[0] # the name of the select with the values is based on header id values = request.form.getlist('header_unique_values') escaped_values = [bleach.clean(v) for v in values] statistic.pivot_values = escaped_values statistic.save() flash('Table settings saved successfully', 'success') return redirect(url_for('statistics' , org_label=org.label , project_label=project.label , statistic_label=statistic.label , edit='true')) elif form_type == 'filters': # extract the 'any filters' vs 'all' distinction filter_settings = request.form.get('apply_any_filters', '') if filter_settings == 'true': statistic.apply_any_filters = True else: statistic.apply_any_filters = False statistic.save() # attach filter to statistic requested_filter_ids = request.form.getlist('filters') attached_filters = [] for requested_id in requested_filter_ids: prefix, filter_id = requested_id.split('__') filters = Filter.objects(id=filter_id) if not filters: abort(404) attached_filters.append(filters[0]) statistic.update(set__filters = attached_filters) return redirect(url_for('statistics', org_label=org.label , project_label=project.label , statistic_label=statistic.label, edit='true')) elif form_type == 'admin': # delete the statistic name = statistic.name utilities.delete_statistic(statistic, session['email']) flash('statistic "%s" was deleted successfully' % name, 'success') return redirect(url_for('statistics', org_label=org.label , project_label=project.label)) else: # bad 'form_type' abort(404) try: statistic.save() flash('Changes saved successfully', 'success') return redirect(url_for('statistics', org_label=org.label , project_label=project.label , statistic_label=statistic.label, edit='true')) except: app.logger.error('%s experienced an error saving info about %s' % ( session['email'], request.form['name'])) flash('Error saving changes -- make sure statistic names are \ unique.', 'error') return redirect(url_for('statistics', org_label=org.label , project_label=project.label, statistic_label=statistic_label , edit='true')) if request.method == 'GET': if statistic_label: statistics = Statistic.objects(label=statistic_label , project=project) if not statistics: app.logger.error('%s tried to access a statistic that does \ not exist' % session['email']) flash('Statistic "%s" not found, sorry!' % statistic_label , 'warning') return redirect(url_for('projects', org_label=org.label , project_label=project.label)) statistic = statistics[0] if request.args.get('edit', '') == 'true': # valid statistic types statistic_types = constants.statistic_types available_filters = Filter.objects(project=project) # find all string-type headers # these are available for compute_multiple string_type_headers = [] for header in project.ordered_schema: if header.display and header.data_type == 'string': string_type_headers.append(header) return render_template('statistic_edit.html' , statistic = statistic , statistic_types = statistic_types , available_filters = available_filters , string_type_headers = string_type_headers) else: # apply the filters and show matches conditions = { 'project': project , 'unique': True , 'visible': True } entries = Entry.objects(**conditions) # sort the entries based on project defaults entries = utilities.sort_entries(project, entries) project_count = len(entries) for filter in statistic.filters: if not filter.comparison: flash('filter "%s" does not have a complete \ definition' % filter.name, 'warning') return render_template('statistic.html' , statistic=statistic , entries=entries , project_count=project_count) # apply relevant filters and then computes the statistic if statistic.pivot: # returns dict keyed by the statistic's pivot_values result = utilities.compute_pivot_statistic(statistic , entries) else: # returns a single result result = utilities.compute_statistic(statistic, entries) # find matches to the applied filters matches = utilities.apply_filters(statistic.filters, entries , statistic.apply_any_filters) total_matches = len(matches) # pagination entries_per_page = 10 pages = utilities.calculate_pages(len(matches) , entries_per_page=entries_per_page) # validate the requested page current_page = utilities.validate_page_request( request.args.get('page', 1), pages) # manually paginate start_index = entries_per_page * (current_page - 1) end_index = start_index + entries_per_page paginated_matches = matches[start_index:end_index] return render_template('statistic.html' , statistic=statistic , entries=paginated_matches , total_matches = total_matches , project_count=project_count , result=result , current_page = current_page , number_of_pages = pages) if request.args.get('create', '') == 'true': # create a new statistic # CSRF validation token = request.args.get('token', '') if not verify_token(token): abort(403) try: statistic_name = 'stat-%s' \ % utilities.generate_random_string(6) new_statistic= Statistic( creation_time = datetime.datetime.utcnow() , creator = user , label = statistic_name.lower() , project = project , name = statistic_name ) new_statistic.save() app.logger.info('statistic created by %s' % session['email']) flash('statistic created; please change the defaults' , 'success') except: app.logger.error('statistic creation failed for %s' % \ session['email']) flash('There was an error, sorry :/', 'error') return redirect(url_for('projects', org_label=org.label , project=project.label)) # redirect to the editing screen return redirect(url_for('statistics', org_label=org.label , project_label=project.label , statistic_label=new_statistic.label, edit='true')) # no statistic in particular was specified; show em all statistics = Statistic.objects(project=project) # calculate the results of these statistics statistic_results = {} unique_entries = Entry.objects(project=project, unique=True , visible=True) for statistic in statistics: statistic_results[statistic.name] = \ utilities.compute_statistic(statistic, unique_entries) return render_template('project_statistics.html', project=project , statistics=statistics, statistic_results=statistic_results)