Пример #1
0
 def test_parse_options(self):
     db = self.env.get_db_cnx()
     options, query_args = parse_options(
         db,
         "milestone=milestone1, startdate=2008-02-20, enddate=2008-02-28",
         {})
     self.assertNotEqual(query_args['milestone'], None)
     self.assertNotEqual(options['startdate'], None)
     self.assertNotEqual(options['enddate'], None)
Пример #2
0
    def render_macro(self, req, name, content):
        db = self.env.get_db_cnx()
        # prepare options
        options, query_args = parse_options(db, content,
                                            copy.copy(DEFAULT_OPTIONS))

        query_args[self.estimation_field + "!"] = None
        tickets = execute_query(self.env, req, query_args)

        sum = 0.0
        estimations = {}
        for ticket in tickets:
            try:
                estimation = float(ticket[self.estimation_field])
                owner = ticket['owner']
                sum += estimation
                if estimations.has_key(owner):
                    estimations[owner] += estimation
                else:
                    estimations[owner] = estimation
            except:
                pass

        estimations_string = []
        labels = []
        for owner, estimation in estimations.iteritems():
            labels.append(
                "%s %s%s" %
                (owner, str(int(estimation)), self.estimation_suffix))
            estimations_string.append(str(int(estimation)))

        # Title
        title = 'Workload'

        # calculate remaining work time
        if options.get('today') and options.get('enddate'):
            currentdate = options['today']
            day = timedelta(days=1)
            days_remaining = 0
            while currentdate <= options['enddate']:
                if currentdate.weekday() < 5:
                    days_remaining += 1
                currentdate += day
            title += ' %s%s (%s workdays left)' % (
                int(sum), self.estimation_suffix, days_remaining)

        return Markup(
            "<img src=\"http://chart.apis.google.com/chart?"
            "chs=%sx%s"
            "&amp;chd=t:%s"
            "&amp;cht=p3"
            "&amp;chtt=%s"
            "&amp;chl=%s"
            "&amp;chco=%s\" "
            "alt=\'Workload Chart\' />" %
            (options['width'], options['height'], ",".join(estimations_string),
             title, "|".join(labels), options['color']))
 def test_build_empty_chart(self):
     chart = BurndownChart(self.env)
     db = self.env.get_db_cnx()
     options, query_args = parse_options(db, "milestone=milestone1, startdate=2008-02-20, enddate=2008-02-28", {})
     timetable = chart._calculate_timetable(options, query_args, self.req)
     xdata, ydata, maxhours = chart._scale_data(timetable, options)
     self.assertEqual(xdata, ['0.00', '12.50', '25.00', '37.50', '50.00', '62.50', '75.00', '87.50', '100.00'])
     self.assertEqual(ydata, ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'])
     self.assertEqual(maxhours, Decimal(100))
Пример #4
0
 def test_build_empty_chart(self):
     chart = BurndownChart(self.env)
     db = self.env.get_db_cnx()
     options, query_args = parse_options(db, "milestone=milestone1, startdate=2008-02-20, enddate=2008-02-28", {})
     timetable, _ = chart._calculate_timetable(options, query_args, self.req)
     dates = sorted(timetable.keys())
     xdata, ydata, maxhours = chart._scale_data(timetable, dates, Decimal(0), options)
     self.assertEqual(xdata, ['0.00', '12.50', '25.00', '37.50', '50.00', '62.50', '75.00', '87.50', '100.00'])
     self.assertEqual(ydata, ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'])
     self.assertEqual(maxhours, Decimal(100))
 def test_build_empty_chart(self):
     chart = BurndownChart(self.env)
     str = "milestone=milestone1, startdate=2008-02-20, enddate=2008-02-28"
     options, query_args = parse_options(self.env, str, {})
     timetable = chart._calculate_timetable(options, query_args, self.req)
     xdata, ydata, maxhours = chart._scale_data(timetable, options)
     self.assertEqual(xdata,
                      ['0.00', '12.50', '25.00', '37.50', '50.00', '62.50',
                       '75.00', '87.50', '100.00'])
     self.assertEqual(ydata, ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00',
                              '0.00', '0.00', '0.00'])
     self.assertEqual(maxhours, Decimal(100))
Пример #6
0
    def render_macro(self, req, name, content):
        db = self.env.get_db_cnx()
        # prepare options
        options, query_args = parse_options(db, content, copy.copy(DEFAULT_OPTIONS))
        
        query_args[self.estimation_field + "!"] = None
        tickets = execute_query(self.env, req, query_args)
        
        sum = 0.0
        estimations = {}
        for ticket in tickets:
            try:
                estimation = float(ticket[self.estimation_field])
                owner = ticket['owner']
                sum += estimation
                if estimations.has_key(owner):
                    estimations[owner] += estimation
                else:
                    estimations[owner] = estimation
            except:
                pass

        estimations_string = []
        labels = []
        for owner, estimation in estimations.iteritems():
            labels.append("%s %s%s" % (owner, str(int(estimation)), self.estimation_suffix))
            estimations_string.append(str(int(estimation)))
            
        # Title
        title = 'Workload'

        # calculate remaining work time
        if options.get('today') and options.get('enddate'):
            currentdate = options['today']
            day = timedelta(days=1)
            days_remaining = 0
            while currentdate <= options['enddate']:
                if currentdate.weekday() < 5:
                    days_remaining += 1
                currentdate += day
            title += ' %s%s (%s workdays left)' % (int(sum), self.estimation_suffix, days_remaining)                
                
        return Markup("<img src=\"http://chart.apis.google.com/chart?"
               "chs=%sx%s" 
               "&amp;chd=t:%s"
               "&amp;cht=p3"
               "&amp;chtt=%s"
               "&amp;chl=%s"
               "&amp;chco=%s\" "
               "alt=\'Workload Chart\' />" 
               % (options['width'], options['height'], ",".join(estimations_string), 
                  title, "|".join(labels), options['color']))
    def expand_macro(self, formatter, name, content):

        # prepare options
        req = formatter.req
        options, query_args = parse_options(self.env.get_db_cnx(), content,
                                            copy.copy(DEFAULT_OPTIONS))

        if not options['startdate']:
            raise TracError("No start date specified!")

        # minimum time frame is one day
        if (options['startdate'] >= options['enddate']):
            options['enddate'] = options['startdate'] + timedelta(days=1)

        # calculate data
        timetable = self._calculate_timetable(options, query_args, req)

        # remove weekends
        if not options['weekends']:
            for date in timetable.keys():
                if date.weekday() >= 5:
                    del timetable[date]

        # scale data
        xdata, ydata, maxhours = self._scale_data(timetable, options)

        # build html for google chart api
        dates = sorted(timetable.keys())
        bottomaxis = "0:|" + ("|").join([str(date.day) for date in dates]) + \
            "|1:|%s/%s|%s/%s" % (dates[0].month, dates[0].year,
                                 dates[ - 1].month, dates[ - 1].year)
        leftaxis = "2,0,%s" % maxhours

        # add line for expected progress
        if options['expected'] == '0':
            expecteddata = ""
        else:
            expecteddata = "|0,100|%s,0" % (round(
                Decimal(options['expected']) * 100 / maxhours, 2))

        # prepare gridlines
        if options['gridlines'] == '0':
            gridlinesdata = "100.0,100.0,1,0"  # create top and right bounding line by using grid
        else:
            gridlinesdata = "%s,%s" % (xdata[1], (round(
                Decimal(options['gridlines']) * 100 / maxhours, 4)))

        # mark weekends
        weekends = []
        saturday = None
        index = 0
        halfday = self._round(Decimal("0.5") / (len(dates) - 1))
        for date in dates:
            if date.weekday() == 5:
                saturday = index
            if saturday and date.weekday() == 6:
                weekends.append(
                    "R,%s,0,%s,%s" %
                    (options['wecolor'],
                     self._round((Decimal(xdata[saturday]) / 100) - halfday),
                     self._round((Decimal(xdata[index]) / 100) + halfday)))
                saturday = None
            index += 1
        # special handling if time period starts with Sundays...
        if len(dates) > 0 and dates[0].weekday() == 6:
            weekends.append("R,%s,0,0.0,%s" % (options['wecolor'], halfday))
        # or ends with Saturday
        if len(dates) > 0 and dates[-1].weekday() == 5:
            weekends.append("R,%s,0,%s,1.0" %
                            (options['wecolor'], Decimal(1) - halfday))

        # chart title
        title = options.get('title', None)
        if title is None and options.get('milestone'):
            title = options['milestone'].split('|')[0]

        chart_args = unicode_urlencode({
            'chs':
            '%sx%s' % (options['width'], options['height']),
            'chf':
            'c,s,%s|bg,s,00000000' % options['bgcolor'],
            'chd':
            't:%s|%s%s' % (",".join(xdata), ",".join(ydata), expecteddata),
            'cht':
            'lxy',
            'chxt':
            'x,x,y',
            'chxl':
            bottomaxis,
            'chxr':
            leftaxis,
            'chm':
            "|".join(weekends),
            'chg':
            gridlinesdata,
            'chco':
            '%s,%s' % (options['color'], options['colorexpected']),
            'chtt':
            title
        })
        self.log.debug("BurndownChart data: %s" % repr(chart_args))
        if self.serverside_charts:
            return tag.image(
                src="%s?data=%s" %
                (req.href.estimationtools('chart'), unicode_quote(chart_args)),
                alt="Burndown Chart (server)")
        else:
            return tag.image(src="http://chart.googleapis.com/chart?%s" %
                             chart_args,
                             alt="Burndown Chart (client)")
    def expand_macro(self, formatter, name, content, args=None):
        req = formatter.req
        # prepare options
        options, query_args = parse_options(self.env, content,
                                            copy.copy(DEFAULT_OPTIONS))

        query_args[self.remaining_field + "!"] = None
        tickets = execute_query(self.env, req, query_args)

        sum = 0.0
        estimations = {}
        for ticket in tickets:
            if ticket['status'] in self.closed_states:
                continue
            try:
                estimation = float(ticket[self.remaining_field])
                owner = ticket['owner']
                sum += estimation
                if owner in estimations:
                    estimations[owner] += estimation
                else:
                    estimations[owner] = estimation
            except:
                pass

        estimations_string = []
        labels = []
        for owner, estimation in estimations.iteritems():
            # Note: Unconditional obfuscation of owner in case it represents
            # an email adress, and as the chart API doesn't support SSL
            # (plain http transfer only, from either client or server).
            labels.append("%s %g%s" % (obfuscate_email_address(owner),
                                       round(estimation, 2),
                                       self.estimation_suffix))
            estimations_string.append(str(int(estimation)))

        # Title
        title = 'Workload'

        # calculate remaining work time
        if options.get('today') and options.get('enddate'):
            currentdate = options['today']
            day = timedelta(days=1)
            days_remaining = 0
            while currentdate <= options['enddate']:
                if currentdate.weekday() < 5:
                    days_remaining += 1
                currentdate += day
            title += ' %g%s (~%s workdays left)' % (round(sum, 2),
                                                    self.estimation_suffix,
                                                    days_remaining)

        chart_args = unicode_urlencode(
            {'chs': '%sx%s' % (options['width'], options['height']),
             'chf': 'bg,s,00000000',
             'chd': 't:%s' % ",".join(estimations_string),
             'cht': 'p3',
             'chtt': title,
             'chl': "|".join(labels),
             'chco': options['color']})
        self.log.debug("WorkloadChart data: %s", chart_args)
        if self.serverside_charts:
            return tag.image(
                src="%s?data=%s" % (req.href.estimationtools('chart'),
                                    unicode_quote(chart_args)),
                alt="Workload Chart (server)")
        else:
            return tag.image(
                src="http://chart.googleapis.com/chart?%s" % chart_args,
                alt="Workload Chart (client)")
    def expand_macro(self, formatter, name, content):
        req = formatter.req
        db = self.env.get_db_cnx()
        # prepare options
        options, query_args = parse_options(db, content, copy.copy(DEFAULT_OPTIONS))

        query_args[self.estimation_field + "!"] = None
        query_args['col'] = '|'.join([self.totalhours_field, 'owner'])
        tickets = execute_query(self.env, req, query_args)

        sum = 0.0
        estimations = {}
        for ticket in tickets:
            if ticket['status'] in self.closed_states:
                continue
            try:
                estimation = float(ticket[self.estimation_field]) - float(ticket[self.totalhours_field])
                owner = ticket['owner']
                sum += estimation
                if estimations.has_key(owner):
                    estimations[owner] += estimation
                else:
                    estimations[owner] = estimation
            except:
                pass

        data = []
        data.append(['Owner', 'Workload'])

        for owner, estimation in estimations.iteritems():
            estimation = max(0, estimation)
            label = "%s %g%s" % (obfuscate_email_address(owner),
                            round(estimation, 2),
                            self.estimation_suffix)
            data.append([label, float(estimation)])

        # Title
        title = 'Workload'

        # calculate remaining work time
        if options.get('today') and options.get('enddate'):
            currentdate = options['today']
            day = timedelta(days=1)
            days_remaining = 0
            while currentdate <= options['enddate']:
                if currentdate.weekday() < 5:
                    days_remaining += 1
                currentdate += day
            title += ' %g%s (~%s workdays left)' % (round(sum, 2),
                                    self.estimation_suffix, days_remaining)

        element_id = 'chart-%d' % random.randint(0, 0xffffffff)
        args = {
            'containerId': element_id,
            'chartType': 'PieChart',
            'options': {
                'width': int(options['width']),
                'height': int(options['height']),
                'title': title,
                'legend': { 'position': 'labeled' },
                'pieSliceText': 'none',
                'tooltip': 'percentage',
            },
        }
        script = "EstimationCharts.push(function() {\n"
        script += 'var data=' + to_json(data) + ";\n"
        script += 'var args=' + to_json(args) + ";\n"
        script += 'DrawWorkloadChart(data, args);'
        script += '});'

        return tag.div(tag.div(id=element_id), tag.script(script))
    def expand_macro(self, formatter, name, content, args=None):

        # prepare options
        req = formatter.req
        options, query_args = parse_options(self.env, content,
                                            copy.copy(DEFAULT_OPTIONS))

        if not options['startdate']:
            raise TracError("No start date specified!")

        # minimum time frame is one day
        if options['startdate'] >= options['enddate']:
            options['enddate'] = options['startdate'] + timedelta(days=1)

        # calculate data
        timetable = self._calculate_timetable(options, query_args, req)
        timetable_spent = self._calculate_timetable_spent(options, query_args, req)
        
        # remove weekends
        if not options['weekends']:
            for date in timetable.keys():
                if date.weekday() >= 5:
                    del timetable[date]
                    del timetable_spent[date]

        # scale data
        xdata, ydata, maxhours = self._scale_data(timetable, options)
        xdata_spent, ydata_spent, maxhours_spent = self._scale_data(timetable_spent, options)
        if not options['spent']:
            spentdata = "|0,0|0,0"
        else:
            spentdata = "|%s|%s" % (",".join(xdata_spent), ",".join(ydata_spent))

        # build html for google chart api
        dates = sorted(timetable.keys())
        bottomaxis = "0:|" + "|".join([str(date.day) for date in dates]) + \
                     "|1:|%s/%s|%s/%s" % (dates[0].month, dates[0].year,
                                          dates[- 1].month, dates[- 1].year)
        leftaxis = "2,0,%s" % maxhours

        # add line for expected progress
        if options['expected'] == '0':
            expecteddata = ""
        else:
            expecteddata = "|0,100|%s,0" % (
                round(Decimal(options['expected']) * 100 / maxhours, 2))

        # prepare gridlines
        if options['gridlines'] == '0':
            # create top and right bounding line by using grid
            gridlinesdata = "100.0,100.0,1,0"
        else:
            gridlinesdata = "%s,%s" % (xdata[1], (
                round(Decimal(options['gridlines']) * 100 / maxhours, 4)))

        # mark weekends
        weekends = []
        saturday = None
        index = 0
        halfday = self._round(Decimal("0.5") / (len(dates) - 1))
        for date in dates:
            if date.weekday() == 5:
                saturday = index
            if saturday and date.weekday() == 6:
                weekends.append("R,%s,0,%s,%s" %
                                (options['wecolor'],
                                 self._round((Decimal(
                                     xdata[saturday]) / 100) - halfday),
                                 self._round(
                                     (Decimal(xdata[index]) / 100) + halfday)))
                saturday = None
            index += 1
        # special handling if time period starts with Sundays...
        if len(dates) > 0 and dates[0].weekday() == 6:
            weekends.append("R,%s,0,0.0,%s" % (options['wecolor'], halfday))
        # or ends with Saturday
        if len(dates) > 0 and dates[- 1].weekday() == 5:
            weekends.append(
                "R,%s,0,%s,1.0" % (options['wecolor'], Decimal(1) - halfday))

        # chart title
        title = options.get('title', None)
        if title is None and options.get('milestone'):
            title = options['milestone'].split('|')[0]

        chart_args = unicode_urlencode(
                    {'chs': '%sx%s' % (options['width'], options['height']),
                     'chf': 'c,s,%s|bg,s,00000000' % options['bgcolor'],
                     'chd': 't:%s|%s%s%s' % (",".join(xdata), ",".join(ydata), spentdata, expecteddata),
                     'cht': 'lxy',
                     'chxt': 'x,x,y',
                     'chxl': bottomaxis,
                     'chxr': leftaxis,
                     'chm': "|".join(weekends),
                     'chg': gridlinesdata,
                     'chco': '%s,%s,%s' % (options['color'], options['colorspent'], options['colorexpected']),
                     'chdl': 'Remaining|Spent|Estimated',
                     'chtt': title})
        self.log.debug("BurndownChart data: %s", chart_args)
        if self.serverside_charts:
            return tag.image(
                src="%s?data=%s" % (req.href.estimationtools('chart'),
                                    unicode_quote(chart_args)),
                alt="Burndown Chart (server)")
        else:
            return tag.image(
                src="http://chart.googleapis.com/chart?%s" % chart_args,
                alt="Burndown Chart (client)")
Пример #11
0
 def test_parse_options(self):
     db = self.env.get_db_cnx()
     options, query_args = parse_options(db, "milestone=milestone1, startdate=2008-02-20, enddate=2008-02-28", {})
     self.assertNotEqual(query_args['milestone'], None)
     self.assertNotEqual(options['startdate'], None)
     self.assertNotEqual(options['enddate'], None)
 def test_parse_options(self):
     str = "milestone=milestone1, startdate=2008-02-20, enddate=2008-02-28"
     options, query_args = parse_options(self.env, str, {})
     self.assertNotEqual(query_args['milestone'], None)
     self.assertNotEqual(options['startdate'], None)
     self.assertNotEqual(options['enddate'], None)
Пример #13
0
    def expand_macro(self, formatter, name, content):
        req = formatter.req
        db = self.env.get_db_cnx()
        # prepare options
        options, query_args = parse_options(db, content,
                                            copy.copy(DEFAULT_OPTIONS))

        query_args[self.estimation_field + "!"] = None
        tickets = execute_query(self.env, req, query_args)

        sum = 0.0
        estimations = {}
        for ticket in tickets:
            if ticket['status'] in self.closed_states:
                continue
            try:
                estimation = float(ticket[self.estimation_field] or 0.0)

                if options.get('remainingworkload'):

                    completion_cursor = db.cursor()

                    completion_cursor.execute(
                        "SELECT t.value AS totalhours, c.value AS complete, d.value AS due_close FROM ticket tk LEFT JOIN ticket_custom t ON (tk.id = t.ticket AND t.name = 'totalhours') LEFT JOIN ticket_custom c ON (tk.id = c.ticket AND c.name = 'complete') LEFT JOIN ticket_custom d ON (tk.id = d.ticket AND d.name = 'due_close') WHERE tk.id = %s"
                        % ticket['id'])

                    for row in completion_cursor:

                        ticket['totalhours'], ticket['complete'], ticket[
                            'due_close'] = row
                        break

                    # skip ticket ticket if due date is later than 'enddate':
                    if options.get('showdueonly'):

                        if not ticket['due_close']:
                            continue  # skip tickets with empty ETA when in 'showdueonly' mode
                        due_close = parse_date(ticket['due_close'],
                                               ["%Y/%m/%d"])
                        startdate = options.get('startdate')
                        enddate = options.get('enddate')

                        if startdate and startdate > due_close:
                            continue  # skip tickets with ETA in the past

                        if enddate and enddate < due_close:
                            continue  # skip tickets with ETA in the future

                        pass

                    totalhours = float(ticket['totalhours'] or 0.0)

                    completed = (float(ticket['complete'] or 0.0) /
                                 100) * estimation
                    completed_hours = min(estimation,
                                          max(totalhours, completed))

                    estimation -= completed_hours

                    pass

                owner = ticket['owner']

                sum += estimation
                if estimations.has_key(owner):
                    estimations[owner] += estimation
                else:
                    estimations[owner] = estimation
            except:
                raise

        # Title
        title = 'Workload'

        days_remaining = None

        # calculate remaining work time
        if options.get('today') and options.get('enddate'):
            currentdate = options['today']
            day = timedelta(days=1)
            days_remaining = 0
            while currentdate <= options['enddate']:
                if currentdate.weekday() < 5:
                    days_remaining += 1
                currentdate += day
            title += ' %g%s (~%s workdays left)' % (round(
                sum, 2), self.estimation_suffix, days_remaining)

        estimations_string = []
        labels = []
        workhoursperday = max(float(options.get('workhoursperday')), 0.0)
        chts = '000000'

        for owner, estimation in estimations.iteritems():
            # Note: Unconditional obfuscation of owner in case it represents
            # an email adress, and as the chart API doesn't support SSL
            # (plain http transfer only, from either client or server).
            label = "%s %g%s" % (obfuscate_email_address(owner),
                                 round(estimation, 2), self.estimation_suffix)

            if days_remaining != None:

                user_remaining_hours = days_remaining * workhoursperday

                if not user_remaining_hours or (estimation /
                                                user_remaining_hours) > 1:
                    label = "%s (~%g hours left)!" % (
                        label, round(user_remaining_hours, 2)
                    )  # user does not have enough hours left
                    chts = 'FF0000'  # set chart title style to red
                    pass
                pass

            labels.append(label)

            estimations_string.append(str(int(estimation)))

            pass

        chart_args = unicode_urlencode({
            'chs':
            '%sx%s' % (options['width'], options['height']),
            'chf':
            'bg,s,00000000',
            'chd':
            't:%s' % ",".join(estimations_string),
            'cht':
            'p3',
            'chtt':
            title,
            'chts':
            chts,
            'chl':
            "|".join(labels),
            'chco':
            options['color']
        })

        self.log.debug("WorkloadChart data: %s" % repr(chart_args))
        if self.serverside_charts:
            return tag.image(
                src="%s?data=%s" %
                (req.href.estimationtools('chart'), unicode_quote(chart_args)),
                alt="Workload Chart (server)")
        else:
            return tag.image(src="https://chart.googleapis.com/chart?%s" %
                             chart_args,
                             alt="Workload Chart (client)")
Пример #14
0
    def render_macro(self, req, name, content):

        # prepare options
        options, query_args = parse_options(self.env.get_db_cnx(), content,
                                            copy.copy(DEFAULT_OPTIONS))

        if not options['startdate']:
            raise TracError("No start date specified!")

        # minimum time frame is one day
        if (options['startdate'] >= options['enddate']):
            options['enddate'] = options['startdate'] + timedelta(days=1)

        change = options['change']

        # calculate data
        timetable, delta = self._calculate_timetable(options, query_args, req)
        timetable_less_change = {}

        if change:

            cumulative_change = Decimal(0)
            for current_date in sorted(timetable.keys()):
                cumulative_change += delta.get(current_date, Decimal(0))
                timetable_less_change[
                    current_date] = timetable[current_date] - cumulative_change

        dates = sorted(timetable.keys())

        # build html for google chart api

        chart_params = {}
        chart_params['cht'] = 'lxy'
        chart_params['chtt'] = self._get_title(options)
        chart_params['chco'] = options['color']
        chart_params['chs'] = "%sx%s" % (options['width'], options['height'])
        chart_params[
            'chg'] = "100.0,100.0,1,0"  # create top and right bounding line by using grid"
        chart_params['chxt'] = "x,x,x,y"

        # Add scaled data
        try:
            maxhours = max(timetable.values())
        except ValueError:
            maxhours = 0

        try:
            cmaxhours = max(timetable_less_change.values())
        except ValueError:
            cmaxhours = 0

        real_maxhours = max(maxhours, cmaxhours)
        real_maxhours = Decimal(real_maxhours)
        if real_maxhours <= Decimal(0):
            real_maxhours = Decimal(100)

        xdata, ydata = self._scale_data(timetable, dates, real_maxhours,
                                        options)
        chart_params['chd'] = "t:%s|%s" % (
            ",".join(xdata),
            ",".join(ydata),
        )
        cxdata = cydata = None
        if change:
            cxdata, cydata = self._scale_data(timetable_less_change, dates,
                                              real_maxhours, options)
            chart_params['chd'] += "|%s|%s" % (
                ",".join(cxdata),
                ",".join(cydata),
            )

        if change:
            chart_params['chdl'] = "Current|Less change"

        # Add axes
        chart_params['chxl'] = "0:|" + "|".join([str(date.day) for date in dates]) + \
            "|1:|%s|%s" % (dates[0].month, dates[ - 1].month) + \
            "|2:|%s|%s" % (dates[0].year, dates[ - 1].year)
        chart_params['chxr'] = "3,0,%d" % real_maxhours

        # Add weekends
        downtime = self._mark_downtime(options, dates, xdata, ydata)
        if downtime:
            chart_params['chm'] = '|'.join(downtime)

        return Markup(
            "<img src=\"http://chart.apis.google.com/chart?%s\" alt=\"Burndown Chart\" />"
            % "&amp;".join("%s=%s" % (k, v) for k, v in chart_params.items()))
Пример #15
0
    def render_macro(self, req, name, content):

        # prepare options
        options, query_args = parse_options(self.env.get_db_cnx(), content, copy.copy(DEFAULT_OPTIONS))

        if not options['startdate']:
            raise TracError("No start date specified!")
               
        # minimum time frame is one day
        if (options['startdate'] >= options['enddate']):
            options['enddate'] = options['startdate'] + timedelta(days=1)

        change = options['change']

        # calculate data
        timetable, delta = self._calculate_timetable(options, query_args, req)
        timetable_less_change = {}
        
        if change:
            
            cumulative_change = Decimal(0)
            for current_date in sorted(timetable.keys()):
                cumulative_change += delta.get(current_date, Decimal(0))
                timetable_less_change[current_date] = timetable[current_date] - cumulative_change

        dates = sorted(timetable.keys())

        # build html for google chart api

        chart_params = {}
        chart_params['cht'] = 'lxy'
        chart_params['chtt'] = self._get_title(options)
        chart_params['chco'] = options['color']
        chart_params['chs'] = "%sx%s" % (options['width'], options['height'])
        chart_params['chg'] = "100.0,100.0,1,0"  # create top and right bounding line by using grid"
        chart_params['chxt'] = "x,x,x,y"
        
        # Add scaled data
        try:
	    maxhours = max(timetable.values())
	except ValueError:
	    maxhours = 0

        try:
	    cmaxhours = max(timetable_less_change.values())
	except ValueError:
	    cmaxhours = 0
        

	real_maxhours = max(maxhours, cmaxhours)
	real_maxhours = Decimal(real_maxhours)
	if real_maxhours <= Decimal(0):
	    real_maxhours = Decimal(100)

        xdata, ydata = self._scale_data(timetable, dates, real_maxhours, options)
        chart_params['chd'] = "t:%s|%s" % (",".join(xdata), ",".join(ydata),)
        cxdata = cydata = None
        if change:
            cxdata, cydata = self._scale_data(timetable_less_change, dates, real_maxhours, options)
            chart_params['chd'] += "|%s|%s" % (",".join(cxdata), ",".join(cydata),)
        
        if change:            
            chart_params['chdl'] = "Current|Less change"
        
        # Add axes
        chart_params['chxl'] = "0:|" + "|".join([str(date.day) for date in dates]) + \
            "|1:|%s|%s" % (dates[0].month, dates[ - 1].month) + \
            "|2:|%s|%s" % (dates[0].year, dates[ - 1].year)
        chart_params['chxr'] = "3,0,%d" % real_maxhours
        
        # Add weekends
        downtime = self._mark_downtime(options, dates, xdata, ydata)
        if downtime:
            chart_params['chm'] = '|'.join(downtime)
        
        return Markup("<img src=\"http://chart.apis.google.com/chart?%s\" alt=\"Burndown Chart\" />" % 
                        "&amp;".join("%s=%s" % (k,v) for k,v in chart_params.items()))