view bitten/trac_ext/charts.py @ 196:b5f7d8a4035e

In preparation for supporting BDB XML transactions, move the report store backend selection away from using Trac extension points, and use a plain Python object instead of a component to represent a store backend.
author cmlenz
date Mon, 12 Sep 2005 21:59:14 +0000
parents 439a8bf0bbcd
children e6ddca1e5712
line wrap: on
line source
# -*- coding: iso8859-1 -*-
#
# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://bitten.cmlenz.net/wiki/License.

from datetime import datetime
import re

from trac.core import *
from trac.web import IRequestHandler
from bitten.model import BuildConfig, Build
from bitten.store import get_store
from bitten.trac_ext.api import IReportChartGenerator
from bitten.util import xmlio


class BittenChartRenderer(Component):
    implements(IRequestHandler)

    generators = ExtensionPoint(IReportChartGenerator)

    # IRequestHandler methods

    def match_request(self, req):
        match = re.match(r'/build/([\w.-]+)/chart/(\w+)', req.path_info)
        if match:
            req.args['config'] = match.group(1)
            req.args['type'] = match.group(2)
            return True

    def process_request(self, req):
        report_type = req.args.get('type')
        config = BuildConfig.fetch(self.env, name=req.args.get('config'))

        for generator in self.generators:
            if report_type in generator.get_supported_report_types():
                template = generator.generate_chart_data(req, config,
                                                         report_type)
                break
        else:
            raise TracError, 'Unknown report type "%s"' % report_type

        return template, 'text/xml'


class TestResultsChartGenerator(Component):
    implements(IReportChartGenerator)

    # IReportChartGenerator methods

    def get_supported_report_types(self):
        return ['unittest']

    def generate_chart_data(self, req, config, report_type):
        rev_map = {}
        for build in Build.select(self.env, config=config.name):
            if build.status in (Build.PENDING, Build.IN_PROGRESS):
                continue
            rev_map[str(build.id)] = (build.rev,
                                      datetime.fromtimestamp(build.rev_time))

        store = get_store(self.env)
        xquery = """
for $report in $reports
return
    <tests build="{dbxml:metadata('build', $report)}"
           total="{count($report/test)}"
           failed="{count($report/test[@status='error' or @status='failure'])}">
    </tests>
"""

        # FIXME: It should be possible to aggregate the test counts by revision
        #        in the XQuery above, somehow. For now, we do that in the Python
        #        code

        tests = {} # Accumulated test numbers by revision
        for test in store.query(xquery, config=config, type='unittest'):
            rev, rev_time = rev_map.get(test.attr['build'])
            if rev not in tests:
                tests[rev] = [rev_time, 0, 0]
            tests[rev][1] = max(int(test.attr['total']), tests[rev][1])
            tests[rev][2] = max(int(test.attr['failed']), tests[rev][2])

        tests = [(rev_time, rev, total, failed) for
                 rev, (rev_time, total, failed) in tests.items()]
        tests.sort()

        req.hdf['chart.title'] = 'Unit Tests'
        req.hdf['chart.data'] = [
            [''] + [item[1] for item in tests],
            ['Total'] + [item[2] for item in tests],
            ['Failures'] + [int(item[3]) for item in tests]
        ]

        return 'bitten_chart_tests.cs'


class TestResultsChartGenerator(Component):
    implements(IReportChartGenerator)

    # IReportChartGenerator methods

    def get_supported_report_types(self):
        return ['trace']

    def generate_chart_data(self, req, config, report_type):
        rev_map = {}
        for build in Build.select(self.env, config=config.name):
            if build.status in (Build.PENDING, Build.IN_PROGRESS):
                continue
            rev_map[str(build.id)] = (build.rev,
                                      datetime.fromtimestamp(build.rev_time))

        store = get_store(self.env)
        xquery = """
for $report in $reports
return
    <coverage build="{dbxml:metadata('build', $report)}"
              loc="{count($report/coverage/line)}">
    {
        for $coverage in $report/coverage
        return
            count($coverage/line) * ($coverage/@percentage/text() div 100)
    }
    </coverage>
"""

        # FIXME: It should be possible to aggregate the coverage info by
        #        revision in the XQuery above, somehow. For now, we do that in
        #        the Python code

        coverage = {} # Accumulated coverage info by revision
        for test in store.query(xquery, config=config, type='trace'):
            rev, rev_time = rev_map.get(test.attr['build'])
            if rev not in coverage:
                coverage[rev] = [rev_time, 0, 0]
            coverage[rev][1] = max(int(test.attr['loc']), coverage[rev][1])
            cov_lines = sum([float(val) for val in test.gettext().split()])
            coverage[rev][2] = max(cov_lines, coverage[rev][2])

        coverage = [(rev_time, rev, loc, cov) for
                    rev, (rev_time, loc, cov) in coverage.items()]
        coverage.sort()

        req.hdf['chart.title'] = 'Code Coverage'
        req.hdf['chart.data'] = [
            [''] + [item[1] for item in coverage],
            ['Lines of code'] + [item[2] for item in coverage],
            ['Coverage'] + [int(item[3]) for item in coverage]
        ]

        return 'bitten_chart_coverage.cs'
Copyright (C) 2012-2017 Edgewall Software