Esempio n. 1
0
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
Esempio n. 2
0
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)