Example #1
0
    def test_get_report_by_code_name(self):
        # Couple reports to test with
        class StupidTestReport(base.Report):
            pass
        class NamedStupidReport(base.Report):
            code_name = 'even_more_stupid_name'

        # Nonexistent report returns None
        self.assertEqual(None,
            blingalytics.get_report_by_code_name('nonexistent_report_name'))
        # Automatic code_name returns report
        self.assertEqual(StupidTestReport,
            blingalytics.get_report_by_code_name('stupid_test_report'))
        # User-set code_name returns report
        self.assertEqual(NamedStupidReport,
            blingalytics.get_report_by_code_name('even_more_stupid_name'))
Example #2
0
    def test_get_report_by_code_name(self):
        # Couple reports to test with
        class StupidTestReport(base.Report):
            pass

        class NamedStupidReport(base.Report):
            code_name = 'even_more_stupid_name'

        # Nonexistent report returns None
        self.assertEqual(
            None,
            blingalytics.get_report_by_code_name('nonexistent_report_name'))
        # Automatic code_name returns report
        self.assertEqual(
            StupidTestReport,
            blingalytics.get_report_by_code_name('stupid_test_report'))
        # User-set code_name returns report
        self.assertEqual(
            NamedStupidReport,
            blingalytics.get_report_by_code_name('even_more_stupid_name'))
Example #3
0
def report_response(params, runner=None, cache=DEFAULT_CACHE):
    """
    This frontend helper function is meant to be used in your
    request-processing code to handle all AJAX responses to the Blingalytics
    JavaScript frontend.

    In its most basic usage, you just pass in the request's GET parameters
    as a ``dict``. This will run the report, if required, and then pull the
    appropriate data. It will be returned as a JSON string, which your
    request-processing code should return as an AJAX response. This will vary
    depending what web framework you're using, but it should be pretty simple.

    The function also accepts two options:

    ``runner`` *(optional)*
        If you want your report to run asynchronously so as not to tie up
        your web server workers while processing requests, you can specify a
        runner function. This should be a function that will initiate your
        async processing on a tasks machine or wherever. It will be passed
        two arguments: the report code_name, as a string; and the remaining
        GET parameters so you can process user inputs. By default, no runner
        is used.

    ``cache`` *(optional)*
        By default, this will use a local cache stored at
        ``/tmp/blingalytics_cache``. If you would like to use a different
        cache, simply provide the cache instance.
    """
    # Find and instantitate the report class
    params = dict((k, v) for k, v in params.items())
    report_code_name = params.pop('report', None)
    if not report_code_name:
        return json.dumps({'errors': ['Report code name not specified.']})
    report_cls = get_report_by_code_name(report_code_name)
    if not report_cls:
        return json.dumps({'errors': ['Specified report not found.']})
    report = report_cls(cache)

    # Return immediately for metadata request
    if params.pop('metadata', False):
        return json.dumps({
            'errors': [],
            'widgets': report.render_widgets(),
            'header': report.report_header(),
            'default_sort': report.default_sort,
        })

    # Process user inputs
    errors = report.clean_user_inputs(**params)
    if errors:
        return json.dumps({
            'errors': [str(error) for error in errors],
        })

    # Run the report, either synchronously or not, if needed
    if not report.is_report_finished():
        if runner:
            if not report.is_report_started():
                runner(report_code_name, params)
            return json.dumps({
                'errors': [],
                'poll': True,
            })
        else:
            report.run_report()

    # Return report data
    offset = int(params.get('iDisplayStart'))
    limit = int(params.get('iDisplayLength'))
    sort_col = params.get('iSortCol_0')
    if sort_col:
        sort_col = report.columns[int(sort_col) - 1][0]
    else:
        sort_col = report.default_sort[0]
    sort_dir = str(params.get('sSortDir_0', report.default_sort[1]))
    sort = (sort_col, sort_dir)
    echo = int(params.get('sEcho'))
    return json.dumps({
        'errors': [],
        'poll': False,
        'iTotalRecords': report.report_row_count(),
        'iTotalDisplayRecords': report.report_row_count(),
        'sEcho': str(echo),
        'aaData': report.report_rows(sort=sort, limit=limit, offset=offset),
        'footer': report.report_footer(),
    })
Example #4
0
def report_response(params, runner=None, cache=DEFAULT_CACHE):
    """
    This frontend helper function is meant to be used in your
    request-processing code to handle all AJAX responses to the Blingalytics
    JavaScript frontend.

    In its most basic usage, you just pass in the request's GET parameters
    as a ``dict``. This will run the report, if required, and then pull the
    appropriate data. It will be returned as a JSON string, which your
    request-processing code should return as an AJAX response. This will vary
    depending what web framework you're using, but it should be pretty simple.

    The function also accepts two options:

    ``runner`` *(optional)*
        If you want your report to run asynchronously so as not to tie up
        your web server workers while processing requests, you can specify a
        runner function. This should be a function that will initiate your
        async processing on a tasks machine or wherever. It will be passed
        two arguments: the report code_name, as a string; and the remaining
        GET parameters so you can process user inputs. By default, no runner
        is used.

    ``cache`` *(optional)*
        By default, this will use a local cache stored at
        ``/tmp/blingalytics_cache``. If you would like to use a different
        cache, simply provide the cache instance.
    """
    # Find and instantitate the report class
    params = dict((k, v) for k, v in params.items())
    report_code_name = params.pop('report', None)
    if not report_code_name:
        return json.dumps({'errors': ['Report code name not specified.']})
    report_cls = get_report_by_code_name(report_code_name)
    if not report_cls:
        return json.dumps({'errors': ['Specified report not found.']})
    report = report_cls(cache)

    # Return immediately for metadata request
    if params.pop('metadata', False):
        return json.dumps({
            'errors': [],
            'widgets': report.render_widgets(),
            'header': report.report_header(),
            'default_sort': report.default_sort,
        })

    # Process user inputs
    errors = report.clean_user_inputs(**params)
    if errors:
        return json.dumps({
            'errors': [str(error) for error in errors],
        })

    # Run the report, either synchronously or not, if needed
    if not report.is_report_finished():
        if runner:
            if not report.is_report_started():
                runner(report_code_name, params)
            return json.dumps({
                'errors': [],
                'poll': True,
            })
        else:
            report.run_report()

    # Return report data
    offset = int(params.get('iDisplayStart'))
    limit = int(params.get('iDisplayLength'))
    sort_col = params.get('iSortCol_0')
    if sort_col:
        sort_col = report.columns[int(sort_col) - 1][0]
    else:
        sort_col = report.default_sort[0]
    sort_dir = str(params.get('sSortDir_0', report.default_sort[1]))
    sort = (sort_col, sort_dir)
    echo = int(params.get('sEcho'))
    return json.dumps({
        'errors': [],
        'poll':
        False,
        'iTotalRecords':
        report.report_row_count(),
        'iTotalDisplayRecords':
        report.report_row_count(),
        'sEcho':
        str(echo),
        'aaData':
        report.report_rows(sort=sort, limit=limit, offset=offset),
        'footer':
        report.report_footer(),
    })
Example #5
0
def report_response(params, runner=None, cache=DEFAULT_CACHE):
    """
    This frontend helper function is meant to be used in your
    request-processing code to handle all AJAX responses to the Blingalytics
    JavaScript frontend.

    In its most basic usage, you just pass in the request's GET parameters
    as a ``dict``. This will run the report, if required, and then pull the
    appropriate data. It will return a tuple of three values:

    * The response body content, as a string
    * The response mimetype, as a string
    * A dict of header values to be sent in the response

    Your request-processing code should return the described response. The
    function also accepts two options:

    ``runner`` *(optional)*
        If you want your report to run asynchronously so as not to tie up
        your web server workers while processing requests, you can specify a
        runner function. This should be a function that will initiate your
        async processing on a tasks machine or wherever. It will be passed
        two arguments: the report code_name, as a string; and the remaining
        GET parameters so you can process user inputs. By default, no runner
        is used.

    ``cache`` *(optional)*
        By default, this will use a local cache stored at
        ``/tmp/blingalytics_cache``. If you would like to use a different
        cache, simply provide the cache instance.
    """
    # Find and instantitate the report class
    if hasattr(params, 'iterlists'):
        params = dict([
            (key, (values if len(values) > 1 else values[0]))
            for key, values in params.iterlists()
        ])
    params = dict((k, v) for k, v in params.items())
    report_code_name = params.pop('report', None)
    if not report_code_name:
        return (json.dumps({'errors': ['Report code name not specified.']}),
            'application/javascript', {})
    report_cls = get_report_by_code_name(report_code_name)
    if not report_cls:
        return (json.dumps({'errors': ['Specified report not found.']}),
            'application/javascript', {})
    report = report_cls(cache)

    # Return immediately for metadata request
    if params.pop('metadata', False):
        return (json.dumps({
            'errors': [],
            'widgets': report.render_widgets(),
            'header': report.report_header(),
            'default_sort': report.default_sort,
        }), 'application/javascript', {})

    # Process user inputs
    errors = report.clean_user_inputs(**params)
    if errors:
        return (json.dumps({
            'errors': [str(error) for error in errors],
        }), 'application/javascript', {})

    # Clear cache if requested
    if params.get('killcache', False):
        report.kill_cache()
        return (json.dumps({
            'errors': [],
        }), 'application/javascript', {})

    # Run the report, either synchronously or not, if needed
    if not report.is_report_finished():
        if runner:
            if not report.is_report_started():
                runner(report_code_name, params)
            return (json.dumps({
                'errors': [],
                'poll': True,
            }), 'application/javascript', {})
        else:
            report.run_report()

    # Return full report as downloadable csv if format requested
    if params.get('format') == 'csv':
        if params.get('download', False):
            output = StringIO()
            writer = csv.writer(output)
            writer.writerow([report.display_name])
            writer.writerow(['Timestamp: %s' % report.report_timestamp()
                .strftime('%m-%d-%Y %I:%M %p UTC')])
            writer.writerow([])
            header = report.report_header()
            writer.writerow(filter(
                lambda a: a is not None,
                map(
                    lambda a: a['label'] if not a.get('hidden') else None,
                    header
                )
            ))
            for row in report.report_rows(format='csv'):
                writer.writerow(map(
                    lambda a: a[1],
                    filter(lambda a: not a[0].get('hidden'), zip(header, row))
                ))
            return (output.getvalue(), 'text/csv', {
                'Content-Disposition': 'attachment; filename="%s.csv"' \
                    % report.display_name
            })
        else:
            return (json.dumps({
                'errors': [],
                'poll': False,
            }), 'application/javascript', {})

    # Return report data
    offset = int(params.get('iDisplayStart'))
    limit = int(params.get('iDisplayLength'))
    sort_col = params.get('iSortCol_0')
    if sort_col:
        sort_col = report.columns[int(sort_col) - 1][0]
    else:
        sort_col = report.default_sort[0]
    sort_dir = str(params.get('sSortDir_0', report.default_sort[1]))
    sort = (sort_col, sort_dir)
    echo = int(params.get('sEcho'))
    return (json.dumps({
        'errors': [],
        'poll': False,
        'iTotalRecords': report.report_row_count(),
        'iTotalDisplayRecords': report.report_row_count(),
        'sEcho': str(echo),
        'aaData': report.report_rows(sort=sort, limit=limit, offset=offset),
        'footer': report.report_footer(),
    }), 'application/javascript', {})
Example #6
0
def report_response(params, runner=None, cache=DEFAULT_CACHE):
    """
    This frontend helper function is meant to be used in your
    request-processing code to handle all AJAX responses to the Blingalytics
    JavaScript frontend.

    In its most basic usage, you just pass in the request's GET parameters
    as a ``dict``. This will run the report, if required, and then pull the
    appropriate data. It will return a tuple of three values:

    * The response body content, as a string
    * The response mimetype, as a string
    * A dict of header values to be sent in the response

    Your request-processing code should return the described response. The
    function also accepts two options:

    ``runner`` *(optional)*
        If you want your report to run asynchronously so as not to tie up
        your web server workers while processing requests, you can specify a
        runner function. This should be a function that will initiate your
        async processing on a tasks machine or wherever. It will be passed
        two arguments: the report code_name, as a string; and the remaining
        GET parameters so you can process user inputs. By default, no runner
        is used.

    ``cache`` *(optional)*
        By default, this will use a local cache stored at
        ``/tmp/blingalytics_cache``. If you would like to use a different
        cache, simply provide the cache instance.
    """
    # Find and instantitate the report class
    if hasattr(params, 'iterlists'):
        params = dict([(key, (values if len(values) > 1 else values[0]))
                       for key, values in params.iterlists()])
    params = dict((k, v) for k, v in params.items())
    report_code_name = params.pop('report', None)
    if not report_code_name:
        return (json.dumps({'errors': ['Report code name not specified.']}),
                'application/javascript', {})
    report_cls = get_report_by_code_name(report_code_name)
    if not report_cls:
        return (json.dumps({'errors': ['Specified report not found.']}),
                'application/javascript', {})
    report = report_cls(cache)

    # Return immediately for metadata request
    if params.pop('metadata', False):
        return (json.dumps({
            'errors': [],
            'widgets': report.render_widgets(),
            'header': report.report_header(),
            'default_sort': report.default_sort,
        }), 'application/javascript', {})

    # Process user inputs
    errors = report.clean_user_inputs(**params)
    if errors:
        return (json.dumps({
            'errors': [str(error) for error in errors],
        }), 'application/javascript', {})

    # Clear cache if requested
    if params.get('killcache', False):
        report.kill_cache()
        return (json.dumps({
            'errors': [],
        }), 'application/javascript', {})

    # Run the report, either synchronously or not, if needed
    if not report.is_report_finished():
        if runner:
            if not report.is_report_started():
                runner(report_code_name, params)
            return (json.dumps({
                'errors': [],
                'poll': True,
            }), 'application/javascript', {})
        else:
            report.run_report()

    # Return full report as downloadable csv if format requested
    if params.get('format') == 'csv':
        if params.get('download', False):
            output = StringIO()
            writer = csv.writer(output)
            writer.writerow([report.display_name])
            writer.writerow([
                'Timestamp: %s' %
                report.report_timestamp().strftime('%m-%d-%Y %I:%M %p UTC')
            ])
            writer.writerow([])
            header = report.report_header()
            writer.writerow(
                filter(
                    lambda a: a is not None,
                    map(lambda a: a['label']
                        if not a.get('hidden') else None, header)))
            for row in report.report_rows(format='csv'):
                writer.writerow(
                    map(
                        lambda a: a[1],
                        filter(lambda a: not a[0].get('hidden'),
                               zip(header, row))))
            return (output.getvalue(), 'text/csv', {
                'Content-Disposition': 'attachment; filename="%s.csv"' \
                    % report.display_name
            })
        else:
            return (json.dumps({
                'errors': [],
                'poll': False,
            }), 'application/javascript', {})

    # Return report data
    offset = int(params.get('iDisplayStart'))
    limit = int(params.get('iDisplayLength'))
    sort_col = params.get('iSortCol_0')
    if sort_col:
        sort_col = report.columns[int(sort_col) - 1][0]
    else:
        sort_col = report.default_sort[0]
    sort_dir = str(params.get('sSortDir_0', report.default_sort[1]))
    sort = (sort_col, sort_dir)
    echo = int(params.get('sEcho'))
    return (json.dumps({
        'errors': [],
        'poll':
        False,
        'iTotalRecords':
        report.report_row_count(),
        'iTotalDisplayRecords':
        report.report_row_count(),
        'sEcho':
        str(echo),
        'aaData':
        report.report_rows(sort=sort, limit=limit, offset=offset),
        'footer':
        report.report_footer(),
    }), 'application/javascript', {})