def _group_sprints_by_milestone(self, req, milestones_to_show): """ Returns a dict which holds the sprints grouped by milestone (key: milestone, value: list of tuples (sprint, progress bar data)). The dict contains only sprints for milestones in milestones_to_show. """ # save sprints in dictionary, key is the associated milestone provider = DefaultTicketGroupStatsProvider(self.env) milestone_names = [milestone.name for milestone in milestones_to_show] sprints = {} sp_controller = SprintController(self.env) list_sprints = SprintController.ListSprintsCommand(self.env, criteria={'milestone': 'in %s' % \ milestone_names}) for s in sp_controller.process_command(list_sprints): # milestone_name = sprint.milestone # if (not milestone_name) or (milestone_name not in milestone_names): # continue milestone_name = s.milestone if milestone_name not in sprints: sprints[milestone_name] = [] sprint_stats = self.build_sprint_story_statistics(req, s.name, provider=provider) sprint_data = (s, sprint_stats) sprints[milestone_name].append(sprint_data) return sprints
class AgiloRoadmapModule(RoadmapModule): implements(IRequestHandler) def __init__(self): """Initialize a Sprint Controller""" self.controller = SprintController(self.env) # -------------------------------------------------------------------------- # Felix Schwarz, 11.10.2008: Copied from trac.ticket.roadmap (0.11), we # initially needed our own copy of that because we want to filter the ticket # by sprint name and not by milestone name. Now we want to filter by type, too. def _build_sprint_stats_data(self, req, stat, sprint_name, grouped_by='component', group=None, type_names=None): all_type_names = AgiloConfig(self.env).get_available_types() def query_href(extra_args): args = {'sprint': sprint_name, grouped_by: group, 'group': 'status'} # custom addition to show only certain types if type_names != None: if len(type_names) == 1: args[Key.TYPE] = type_names[0] elif len(type_names) > 1: uninteresting_types = set(all_type_names).difference(set(type_names)) args[Key.TYPE] = ['!' + name for name in uninteresting_types] # end of custom addition args.update(extra_args) return req.href.query(args) return {'stats': stat, 'stats_href': query_href(stat.qry_args), 'interval_hrefs': [query_href(interval['qry_args']) for interval in stat.intervals]} # -------------------------------------------------------------------------- def _build_story_statistics(self, req, stats_provider, sprint_name): """ Assemble statistics for all stories in the given sprint_name so that the progress bar can be displayed. """ sprint_stats = dict() if not sprint_name: return sprint_stats cmd_get_stories = SprintController.ListTicketsHavingPropertiesCommand(self.env, sprint=sprint_name, properties=[Key.STORY_POINTS]) stories = self.controller.process_command(cmd_get_stories) type_dict = dict() closed_stories = list() inprogress_stories = list() for story in stories: if story[Key.STATUS] == Status.CLOSED: closed_stories.append(story) elif story[Key.STATUS] != Status.NEW: inprogress_stories.append(story) type_dict[story[Key.TYPE]] = True type_names = type_dict.keys() number_open_stories = len(stories) - (len(closed_stories) + \ len(inprogress_stories)) try: stat = TicketGroupStats('User Stories status', 'stories') stat.add_interval(_('Completed'), len(closed_stories), qry_args={Key.STATUS: Status.CLOSED}, css_class='closed', overall_completion=True) stat.add_interval(_('In Progress'), len(inprogress_stories), qry_args={Key.STATUS: Status.ACCEPTED}, css_class='inprogress') stat.add_interval(_('Open'), number_open_stories, qry_args={Key.STATUS: [Status.NEW, Status.REOPENED]}, css_class='open') stat.refresh_calcs() sprint_stats = self._build_sprint_stats_data(req, stat, sprint_name, type_names=type_names) except Exception, e: # The DB is closed? And we don't break for statistics error(stats_provider, "ERROR: %s" % to_unicode(e)) return sprint_stats