Changeset 2581
- Timestamp:
- 08/20/07 12:25:18 (1 year ago)
- Files:
-
- tracmetrixplugin/0.11/tracmetrixplugin/mdashboard.py (modified) (4 diffs)
- tracmetrixplugin/0.11/tracmetrixplugin/model.py (modified) (10 diffs)
- tracmetrixplugin/0.11/tracmetrixplugin/templates/mdashboard.html (modified) (2 diffs)
- tracmetrixplugin/0.11/tracmetrixplugin/templates/pdashboard.html (modified) (2 diffs)
- tracmetrixplugin/0.11/tracmetrixplugin/web_ui.py (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
tracmetrixplugin/0.11/tracmetrixplugin/mdashboard.py
r2570 r2581 117 117 event_history = cursor.fetchall() 118 118 119 env.log.info("event_history = %s" % (event_history,))119 #env.log.info("event_history = %s" % (event_history,)) 120 120 121 121 # TODO The tricky thing about this is that we have to deterimine 5 different type of ticket. … … 287 287 288 288 matplotlib.use('Agg') 289 cla()290 fig = figure( )289 #cla() 290 fig = figure(figsize = (6,4)) 291 291 ax = fig.add_subplot(111) # Create supplot with key 111 292 ax.cla() 292 293 ax.plot(numdates, tkt_cummulative_table['Enter'], 'b-') 293 294 ax.plot(numdates, tkt_cummulative_table['Leave'], 'r-') … … 298 299 ax.fmt_xdata = DateFormatter('%Y-%m-%d %H:%M:%S') 299 300 labels = ax.get_xticklabels() 300 setp(labels, rotation=45, fontsize= 8)301 setp(labels, rotation=45, fontsize=6) 301 302 302 303 xlabel('Dates (day)') 303 304 ylabel('Counts (times)') 304 305 title('Cummulative flow chart for ticket status history') 305 legend(('Ticket Entered', 'Ticket Left', 'Ticket Completed'), loc=' upper left')306 legend(('Ticket Entered', 'Ticket Left', 'Ticket Completed'), loc='best') 306 307 307 308 filename = "cummulativeflow_%s" % (milestone.name,) … … 487 488 tkt_history = collect_tickets_status_history(self.env, db, everytickets, milestone) 488 489 490 if tkt_history != {}: 489 491 490 # Sort the key in the history list 491 # returns sorted list of tuple of (key, value) 492 sorted_events = sorted(tkt_history.items(), key=lambda(k,v):(k)) 493 494 #debug 495 for event in sorted_events: 496 self.env.log.info("date: %s: event: %s" % (format_date(to_datetime(event[0])), event[1])) 497 498 499 # Get first date that ticket enter the milestone 500 min_time = min(sorted_events)[0] #in Epoch Seconds 501 begin_date = datetime.fromtimestamp(min_time, utc).date() 502 503 if milestone.completed != None: 504 end_date = milestone.completed 505 else: 506 end_date = datetime.now(utc).date() 507 508 #self.env.log.info("begindate: Timezone %s:%s, UTC:%s)" % \ 509 # (req.tz,datetime.fromtimestamp(min_time, req.tz).date(), \ 510 # datetime.fromtimestamp(min_time, utc).date())) 511 512 #self.env.log.info("enddate: UTC:%s" % (end_date,)) 513 514 515 516 # this is array of date in numpy 517 numdates = drange(begin_date, end_date + timedelta(days=1), timedelta(days=1)) 518 519 tkt_history_table = make_ticket_history_table(self.env, numdates, sorted_events) 520 521 #debug 522 #self.env.log.info("tkt_history_table: %s", (tkt_history_table,)) 523 524 #Create a data for the cumulative flow chart. 525 tkt_cummulative_table = make_cummulative_data(self.env, tkt_history_table) 526 527 #debug 528 #self.env.log.info(tkt_cummulative_table) 529 530 # creat list of dateobject from dates 531 dates = [] 532 for numdate in numdates: 533 534 utc_date = num2date(numdate) 535 dates.append(utc_date) 536 #self.env.log.info("%s: %s" % (utc_date, format_date(utc_date, tzinfo=utc))) 537 538 data['tickethistory'] = tkt_cummulative_table 539 data['dates'] = dates 540 541 create_cummulative_chart(self.env, milestone, numdates, tkt_cummulative_table) 492 # Sort the key in the history list 493 # returns sorted list of tuple of (key, value) 494 sorted_events = sorted(tkt_history.items(), key=lambda(k,v):(k)) 495 496 #debug 497 self.env.log.info("sorted_event content") 498 for event in sorted_events: 499 self.env.log.info("date: %s: event: %s" % (format_date(to_datetime(event[0])), event[1])) 500 501 502 # Get first date that ticket enter the milestone 503 min_time = min(sorted_events)[0] #in Epoch Seconds 504 begin_date = datetime.fromtimestamp(min_time, utc).date() 505 506 if milestone.completed != None: 507 end_date = milestone.completed 508 else: 509 end_date = datetime.now(utc).date() 510 511 #self.env.log.info("begindate: Timezone %s:%s, UTC:%s)" % \ 512 # (req.tz,datetime.fromtimestamp(min_time, req.tz).date(), \ 513 # datetime.fromtimestamp(min_time, utc).date())) 514 515 #self.env.log.info("enddate: UTC:%s" % (end_date,)) 516 517 518 519 # this is array of date in numpy 520 numdates = drange(begin_date, end_date + timedelta(days=1), timedelta(days=1)) 521 522 tkt_history_table = make_ticket_history_table(self.env, numdates, sorted_events) 523 524 #debug 525 #self.env.log.info("tkt_history_table: %s", (tkt_history_table,)) 526 527 #Create a data for the cumulative flow chart. 528 tkt_cummulative_table = make_cummulative_data(self.env, tkt_history_table) 529 530 #debug 531 #self.env.log.info(tkt_cummulative_table) 532 533 # creat list of dateobject from dates 534 dates = [] 535 for numdate in numdates: 536 537 utc_date = num2date(numdate) 538 dates.append(utc_date) 539 #self.env.log.info("%s: %s" % (utc_date, format_date(utc_date, tzinfo=utc))) 540 541 data['tickethistory'] = tkt_cummulative_table 542 data['dates'] = dates 543 544 create_cummulative_chart(self.env, milestone, numdates, tkt_cummulative_table) 542 545 543 546 tracmetrixplugin/0.11/tracmetrixplugin/model.py
r2570 r2581 1 1 import sys 2 import os 3 2 4 from datetime import timedelta, datetime 3 5 from trac.core import * 4 6 from trac.ticket import Ticket, model 5 from trac.util.datefmt import utc 7 from trac.util.datefmt import utc, to_timestamp, to_datetime 6 8 from trac.ticket.roadmap import ITicketGroupStatsProvider, TicketGroupStats 9 10 from bisect import bisect 11 import matplotlib 12 from pylab import * 13 from matplotlib.dates import DayLocator, HourLocator, DateFormatter, drange 7 14 8 15 … … 53 60 return stat 54 61 55 56 class TicketTypeGroupStatsProvider(Component): 57 implements(ITicketGroupStatsProvider) 58 59 def get_ticket_group_stats(self, ticket_ids): 62 def get_ticket_resolution_group_stats(self, ticket_ids): 63 64 # ticket_ids is a list of ticket id as number. 65 total_cnt = len(ticket_ids) 66 if total_cnt: 67 str_ids = [str(x) for x in sorted(ticket_ids)] # create list of ticket id as string 68 cursor = self.env.get_db_cnx().cursor() # get database connection 69 70 type_count = [] # list of dictionary with key name and count 71 72 for type in model.Resolution.select(self.env): 73 74 count = cursor.execute("SELECT count(1) FROM ticket " 75 "WHERE status = 'closed' AND resolution = '%s' AND id IN " 76 "(%s)" % (type.name, ",".join(str_ids))) # execute query and get cursor obj. 77 count = 0 78 for cnt, in cursor: 79 count = cnt 80 81 if count > 0: 82 type_count.append({'name':type.name,'count':count}) 83 84 else: 85 type_count = [] 86 87 stat = TicketGroupStats('ticket resolution', 'ticket') 88 89 90 for type in type_count: 91 92 if type['name'] == 'fixed': # default ticket type 'defect' 93 94 stat.add_interval(type['name'], type['count'], 95 {'type': type['name']}, 'value', True) 96 97 else: 98 stat.add_interval(type['name'], type['count'], 99 {'type': type['name']}, 'waste', False) 100 101 stat.refresh_calcs() 102 return stat 103 104 def get_ticket_type_group_stats(self, ticket_ids): 60 105 61 106 # ticket_ids is a list of ticket id as number. … … 79 124 type_count.append({'name':type.name,'count':count}) 80 125 126 else: 127 type_count = [] 81 128 82 129 stat = TicketGroupStats('ticket type', 'ticket') 130 83 131 84 132 for type in type_count: … … 96 144 return stat 97 145 146 class TicketTypeGroupStatsProvider(Component): 147 implements(ITicketGroupStatsProvider) 148 149 def get_ticket_group_stats(self, ticket_ids): 150 151 # ticket_ids is a list of ticket id as number. 152 total_cnt = len(ticket_ids) 153 if total_cnt: 154 str_ids = [str(x) for x in sorted(ticket_ids)] # create list of ticket id as string 155 cursor = self.env.get_db_cnx().cursor() # get database connection 156 157 type_count = [] # list of dictionary with key name and count 158 159 for type in model.Type.select(self.env): 160 161 count = cursor.execute("SELECT count(1) FROM ticket " 162 "WHERE type = '%s' AND id IN " 163 "(%s)" % (type.name, ",".join(str_ids))) # execute query and get cursor obj. 164 count = 0 165 for cnt, in cursor: 166 count = cnt 167 168 if count > 0: 169 type_count.append({'name':type.name,'count':count}) 170 171 else: 172 type_count = [] 173 174 stat = TicketGroupStats('ticket type', 'ticket') 175 176 177 for type in type_count: 178 179 if type['name'] != 'defect': # default ticket type 'defect' 180 181 stat.add_interval(type['name'], type['count'], 182 {'type': type['name']}, 'value', True) 183 184 else: 185 stat.add_interval(type['name'], type['count'], 186 {'type': type['name']}, 'waste', False) 187 188 stat.refresh_calcs() 189 return stat 190 98 191 class TicketGroupMetrics(object): 99 192 … … 151 244 def get_tickets_created_during(self, start_date, end_date): 152 245 246 end_date = end_date.replace(hour=23, minute=59, second=59) 247 153 248 tkt_ids = [] 154 249 … … 161 256 def get_remaning_opened_ticket_on(self, end_date): 162 257 258 end_date = end_date.replace(hour=23, minute=59, second=59) 259 163 260 tkt_ids = [] 164 261 … … 169 266 170 267 if ticket.values['status'] == 'closed': 171 172 268 173 269 was_opened = True … … 189 285 # Assume that ticket that is not closed are opened 190 286 else: 191 # only add the ticket that was modified before the end date192 if end_date >= ticket.time_c hanged:287 # only add the ticket that was created before the end date 288 if end_date >= ticket.time_created: 193 289 tkt_ids.append(ticket.id) 194 195 196 self.env.log.info(tkt_ids) 197 290 198 291 return tkt_ids 199 200 292 201 293 202 294 def get_tickets_closed_during(self, start_date, end_date): 295 296 end_date = end_date.replace(hour=23, minute=59, second=59) 203 297 204 298 tkt_ids = [] … … 228 322 closed_tickets, 229 323 float(len(closed_tickets)) * 100 / float(len(opened_tickets))) 230 324 325 def get_daily_backlog_history(self, start_date, end_date): 326 """ 327 returns list of tuple (date,stats) 328 date is date value in epoc time 329 stats is dictionary of {'created':[], 'opened':[], 'closed':[]} 330 """ 331 332 # this is array of date in numpy 333 numdates = drange(start_date, end_date + timedelta(days=1), timedelta(days=1)) 334 335 # for date in numdates: 336 # self.env.log.info(num2date(date)) 337 338 end_date = end_date.replace(hour=23, minute=59, second=59) 339 340 341 # each key is the list of list of ticket. The index of the list is corresponding 342 # to the index of the date in numdates list. 343 backlog_stats = {'created':[], 'opened':[], 'closed':[]} 344 345 # initialize backlog_stats 346 347 for date in numdates: 348 for key in backlog_stats: 349 backlog_stats[key].append([]) 350 351 # start by getting the list of opened ticket at the end of the start date. 352 backlog_stats['opened'][0] = self.get_remaning_opened_ticket_on(start_date) 353 354 for ticket in self.tickets: 355 356 # only consider the ticket that was created before end dates. 357 if ticket.time_created <= end_date: 358 359 # only track the ticket that create since start_date 360 if ticket.time_created >= start_date: 361 # determine index 362 date = ticket.time_created.date() 363 #get index of day in the dates list 364 index = bisect(numdates, date2num(date)) - 1 365 366 # add ticket created ticket list 367 backlog_stats['created'][index].append(ticket.id) 368 369 for t, author, field, oldvalue, newvalue, permanent in ticket.get_changelog(): 370 371 # determine index 372 date = t.date() 373 #get index of day in the dates list 374 index = bisect(numdates, date2num(date)) - 1 375 376 if field == 'status' and start_date <= t <= end_date: 377 378 if newvalue == 'closed': 379 # add ticket created ticket list 380 backlog_stats['closed'][index].append(ticket.id) 381 382 elif newvalue == 'reopen': 383 backlog_stats['opened'][index].append(ticket.id) 384 385 # update opened ticket list 386 for idx, list in enumerate(backlog_stats['opened']): 387 388 if idx > 0: 389 390 # merge list of opened ticket from previous day 391 list.extend(backlog_stats['opened'][idx-1]) 392 393 # add created ticket to opened ticket list 394 list.extend(backlog_stats['created'][idx]) 395 396 # remove closed ticket from opened ticket list. 397 for id in backlog_stats['closed'][idx]: 398 list.remove(id) 399 400 list.sort() 401 402 # for idx, numdate in enumerate(numdates): 403 # self.env.log.info(num2date(numdate)) 404 # self.env.log.info(backlog_stats['created'][idx]) 405 # self.env.log.info(backlog_stats['opened'][idx]) 406 # self.env.log.info(backlog_stats['closed'][idx]) 407 408 return (numdates, backlog_stats) 409 410 def get_daily_backlog_chart(self, backlog_history): 411 412 numdates = backlog_history[0] 413 backlog_stats = backlog_history[1] 414 415 # create counted list. 416 opened_tickets_dataset = [len(list) for list in backlog_stats['opened']] 417 created_tickets_dataset = [len(list) for list in backlog_stats['created']] 418 419 # need to add create and closed ticket for charting purpose. We want to show 420 # closed tickets on top of opened ticket in bar chart. 421 closed_tickets_dataset = [] 422 for i in range(len(created_tickets_dataset)): 423 closed_tickets_dataset.append(created_tickets_dataset[i] + len(backlog_stats['closed'][i])) 424 425 bmi_dataset = [] 426 for i in range(len(opened_tickets_dataset)): 427 bmi_dataset.append(float(closed_tickets_dataset[i])*100/float(opened_tickets_dataset[i])) 428 429 # for idx, numdate in enumerate(numdates): 430 # self.env.log.info("%s: %s, %s, %s" % (num2date(numdate), 431 # closed_tickets_dataset[idx], 432 # opened_tickets_dataset[idx], 433 # created_tickets_dataset[idx])) 434 435 matplotlib.use('Agg') 436 #cla() 437 fig = figure(figsize = (6,4)) 438 ax = fig.add_subplot(111) # Create supplot with key 111 439 line1 = ax.plot_date(numdates, opened_tickets_dataset, '-') 440 line2 = ax.bar(numdates, closed_tickets_dataset, 0.5, color='#bae0ba') 441 line3 = ax.bar(numdates, created_tickets_dataset, 0.5, color='#9966ff') 442 ax.set_xlim( numdates[0], numdates[-1] ) 443 ax.xaxis.set_major_locator(DayLocator()) 444 ax.xaxis.set_major_formatter( DateFormatter('%Y-%m-%d')) 445 labels = ax.get_xticklabels() 446 setp(labels, rotation=45, fontsize=6) 447 448 xlabel('Dates (day)') 449 ylabel('Number of tickets') 450 title('Opened/Closed Tickets') 451 ax.legend((line1, line2[0], line3[0]), ('Opened Tickets', 'Closed/day', 'Created/day'), loc='best') 452 453 filename = "dailybacklog" 454 path = os.path.join(self.env.path, 'cache', 'tracmetrixplugin', filename) 455 456 fig.savefig(path) 457 458 return path 459 231 460 class TicketMetrics(object): 232 461 … … 349 578 stdev = (sdsq / (len(self.sequence) - 1 or 1)) ** .5 350 579 return stdev 580 581 class ChangesetsStats: 582 583 def __init__(self, env, start_date=None, stop_date=None): 584 585 self.env = env 586 587 self.start_date = start_date 588 self.stop_date = stop_date 589 self.first_rev = self.last_rev = None 590 591 if start_date != None and stop_date !=None: 592 self.set_date_range(start_date, stop_date) 593 594 def set_date_range(self, start_date, stop_date): 595 596 cursor = self.env.get_db_cnx().cursor() 597 cursor.execute("SELECT rev, time, author FROM revision " 598 "WHERE time >= %s AND time < %s ORDER BY time", 599 (to_timestamp(start_date), to_timestamp(stop_date))) 600 601 self.changesets = [] 602 for rev, time, author in cursor: 603 self.changesets.append((rev,time,author)) 604 605 self.start_date = start_date 606 self.stop_date = stop_date 607 self.first_rev = self.changesets[0][0] 608 self.last_rev = self.changesets[-1][0] 609 610 def get_commit_by_date(self): 611 612 numdates = drange(self.start_date, self.stop_date, timedelta(days=1)) 613 614 numcommits = [0 for i in numdates] 615 616 for rev, time, author in self.changesets: 617 618 date = to_datetime(time, utc).date() 619 #get index of day in the dates list 620 index = bisect(numdates, date2num(date)) - 1 621 622 numcommits[index] += 1 623 624 return (numdates, numcommits) 625 626 627 def get_commit_by_date_chart(self, commit_history): 628 629 numdates = commit_history[0] 630 numcommits = commit_history[1] 631 632 matplotlib.use('Agg') 633 #cla() 634 fig = figure(figsize = (6,4)) 635 ax = fig.add_subplot(111) # Create supplot with key 111 636 line1 = ax.bar(numdates, numcommits, 0.5, color='#9966ff') 637 ax.set_xlim( numdates[0], numdates[-1] ) 638 ax.xaxis.set_major_locator(DayLocator()) 639 ax.xaxis.set_major_formatter( DateFormatter('%Y-%m-%d')) 640 labels = ax.get_xticklabels() 641 setp(labels, rotation=45, fontsize=6) 642 643 xlabel('Dates (day)') 644 ylabel('Number of commits') 645 title('Commits by Date') 646 647 filename = "commitsbydate" 648 path = os.path.join(self.env.path, 'cache', 'tracmetrixplugin', filename) 649 650 fig.savefig(path) 651 652 return path 653 654 tracmetrixplugin/0.11/tracmetrixplugin/templates/mdashboard.html
r2529 r2581 56 56 57 57 </div> 58 59 58 59 <div> <img src="${href.mdashboard('%s/cummulativeflow.png' % (milestone.name,))}"/></div> 60 60 <div class="table"> 61 61 <h2>Ticket History Table</h2> … … 81 81 </div> 82 82 </div> 83 84 85 86 <div> <img src="${href.mdashboard('%s/cummulativeflow.png' % (milestone.name,))}"/></div>87 88 83 89 84 tracmetrixplugin/0.11/tracmetrixplugin/templates/pdashboard.html
r2570 r2581 20 20 21 21 <div class="info"> 22 <h2>Project Tickets Statistics</h2> 23 <py:if test="proj_progress_stat.stats.count">${progress_bar(proj_progress_stat.stats, proj_progress_stat.interval_hrefs, stats_href=proj_progress_stat.stats_href)}</py:if> 24 25 <h2>Project Backlog Metrics</h2> 26 <table style="width: 800px" class="listing project stats"> 27 <thead> 28 <tr> 29 <th style="width: 90px" class="style1">Month</th> 30 <th style="width: 200px">Ticket Created</th> 31 <th style="width: 200px">Ticket Opened</th> 32 <th style="width: 200px">Ticket Closed</th> 33 <th style="width: 90px">Backlog Management Index (Closed/Opened)</th> 34 </tr> 35 </thead> 36 <tbody> 37 <tr py:for="bmi in project_bmi_stats" class="statistics"> 38 <td style="width: 90px" class="style1">${bmi[0]}</td> 39 <td style="width: 200px">${len(bmi[1])} <br/> ${wiki_to_html(context(), ', '.join(['#%s' % i for i in bmi[1]]))}</td> 40 <td style="width: 200px">${len(bmi[2])} <br/> ${wiki_to_html(context(), ', '.join(['#%s' % i for i in bmi[2]]))}</td> 41 <td style="width: 200px">${len(bmi[3])} <br/> ${wiki_to_html(context(), ', '.join(['#%s' % i for i in bmi[3]]))}</td> 42 <td style="width: 90px">${"%.2f %%" % (bmi[4],)}</td> 43 </tr> 44 </tbody> 45 </table> 22 <h2>Project Tickets Statistics</h2> 23 <br/><b>Tickets by Status</b> 24 <py:if test="proj_progress_stat.stats.count">${progress_bar(proj_progress_stat.stats, proj_progress_stat.interval_hrefs, stats_href=proj_progress_stat.stats_href)}</py:if> 25 26 <br/><b>Tickets by Resolution</b> 27 <py:if test="proj_progress_stat.stats.count">${progress_bar(proj_closed_stat.stats, proj_closed_stat.interval_hrefs, stats_href=proj_closed_stat.stats_href)}</py:if> 28 29 </div> 30 31 <div> 32 33 <h2>Project Backlog Metrics</h2> 34 35 <h3> Daily Backlog Statistics </h3> 36 <img src="${href.pdashboard('dailybacklog.png')}" alt="Backlog Graph"/> 37 38 <h3> Monthly Backlog Metrics </h3> 39 <table style="width: 800px" class="listing project stats"> 40 <thead> 41 <tr> 42 <th style="width: 90px">Month</th> 43 <th style="width: 200px">Ticket Created</th> 44 <th style="width: 200px">Ticket Opened</th> 45 <th style="width: 200px">Ticket Closed</th> 46 <th style="width: 90px">Backlog Management Index (Closed/Opened)</th> 47 </tr> 48 </thead> 49 <tbody> 50 <tr py:for="bmi in project_bmi_stats" > 51 <td style="width: 90px">${bmi[0]}</td> 52 <td style="width: 200px">${len(bmi[1])} ${wiki_to_html(context(), ', '.join(['#%s' % i for i in bmi[1]]))}</td> 53 <td style="width: 200px">${len(bmi[2])} ${wiki_to_html(context(), ', '.join(['#%s' % i for i in bmi[2]]))}</td> 54 <td style="width: 200px">${len(bmi[3])} ${wiki_to_html(context(), ', '.join(['#%s' % i for i in bmi[3]]))}</td> 55 <td style="width: 90px">${"%.2f %%" % (float(len(bmi[3])) * 100 / float(len(bmi[2])),)}</td> 56 </tr> 57 </tbody> 58 </table> 46 59 </div> 47 60 <br/> … … 138 151 </div> 139 152 153 <div> 154 <h2> Repository Statistics </h2> 155 <img src="${href.pdashboard('commitsbydate.png')}" alt="Commits by Date"/> 156 </div> 157 140 158 <div id="help"><strong>Note:</strong> See 141 159 <a href="${href.wiki('TracRoadmap')}">TracRoadmap</a> for help on using tracmetrixplugin/0.11/tracmetrixplugin/web_ui.py
r2570 r2581 37 37 cursor = env.get_db_cnx().cursor() 38 38 39 cursor.execute("SELECT id FROM ticket ")39 cursor.execute("SELECT id FROM ticket ORDER BY id") 40 40 41 41 tkt_ids = [id for id , in cursor] … … 45 45 def last_day_of_month(year, month): 46 46 47 return datetime(year, month+1, 1, tzinfo=utc) - timedelta(seconds=1) 48 47 return datetime(year, month+1, 1, tzinfo=utc) - timedelta(days=1) 49 48 50 49 class PDashboard(Component): … … 80 79 self.env.log.info("pdashboard match request %s" % (req.path_info,)) 81 80 82 return re.match(r'/pdashboard/?', req.path_info) is not None 81 urlcomp = req.path_info.split('/') 82 83 self.env.log.info(urlcomp) 84 85 if urlcomp[1] == 'pdashboard': 86 if len(urlcomp) == 3: #url has 2 87 req.args['imagename'] = urlcomp[2] 88 else: 89 req.args['imagename'] = None 90 91 return True 83 92 84 93 def process_request(self, req): 85 94 req.perm.require('ROADMAP_VIEW') 86 95 96 db = self.env.get_db_cnx() 97 98 filename = req.args.get('imagename') 99 100 if filename != None: 101 102 self.env.log.info("request for image") 103 path = os.path.join(self.env.path, 'cache', 'tracmetrixplugin', filename) 104 req.send_file(path, mimeview.get_mimetype(path)) 105 106 else: 107 108 self.env.log.info("request mdashboard") 109 add_stylesheet(req, 'pd/css/dashboard.css') 110 111 return self._render_view(req, db) 112 113 114 def _render_view(self, req, db): 115 87 116 showall = req.args.get('show') == 'all' 88 89 db = self.env.get_db_cnx() 90 117 91 118 # Get list of milestone object for the project 92 119 milestones = list(Milestone.select(self.env, showall, db)) … … 104 131 'description': self.env.project_description 105 132 } 106 107 108 133 109 134 data = { … … 127 152 for interval in proj_stat.intervals]} 128 153 129 154 closed_stat = self.stats_provider.get_ticket_resolution_group_stats(project_tickets) 155 156 data['proj_closed_stat'] = {'stats': closed_stat, 157 'stats_href': req.href.query(closed_stat.qry_args), 158 'interval_hrefs': [req.href.query(interval['qry_args']) 159 for interval in closed_stat.intervals]} 160 161 130 162 tkt_group_metrics = TicketGroupMetrics(self.env, project_tickets) 131 163 … … 145 177 last_day = last_day_of_month(today.year, today.month-1) 146 178 bmi_stats.append(tkt_group_metrics.get_bmi_monthly_stats(first_day, last_day)) 179 180 # get daily stat from today and a month back 181 last_day = datetime(today.year, today.month, today.day, tzinfo=utc) 182 first_day = datetime(today.year, today.month-1, today.day, tzinfo=utc) 183 184 backlog_history = tkt_group_metrics.get_daily_backlog_history(first_day, last_day) 185 daily_backlog_chart_path = tkt_group_metrics.get_daily_backlog_chart(backlog_history) 186 187 changeset_group_stats = ChangesetsStats(self.env, first_day, last_day) 188 commits_by_date = changeset_group_stats.get_commit_by_date() 189 commits_by_date_chart = changeset_group_stats.get_commit_by_date_chart(commits_by_date) 147 190 148 191 data['project_bmi_stats'] = bmi_stats
