Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
 def actual_burndown(self, filter_by_component=None):
     command = SprintController.GetActualBurndownCommand(
         self.env,
         sprint=self.sprint.name,
         filter_by_component=filter_by_component,
         remaining_field=BurndownDataConstants.REMAINING_TIME)
     return SprintController(self.env).process_command(command)
Ejemplo n.º 3
0
 def _sprint(self, req, sprint_name):
     try:
         get_sprint = SprintController.GetSprintCommand(self.env,
                                                        sprint=sprint_name)
         get_sprint.native = True
         return SprintController(self.env).process_command(get_sprint)
     except ICommand.NotValidError, e:
         self.error_response(req, {}, [exception_to_unicode(e)])
Ejemplo n.º 4
0
 def _do_delete(self, req, name):
     """Deletes and retarget tickets to a new sprint if specified"""
     self._do_retarget(req, name)
     cmd_delete = SprintController.DeleteSprintCommand(self.env,
                                                       sprint=name)
     self.controller.process_command(cmd_delete)
     req.redirect(req.href.roadmap())
Ejemplo n.º 5
0
 def _do_retarget(self, req, name):
     """Perform a retarget if selected in the request"""
     retarget = req.args.get('retarget')
     if retarget:
         cmd_retarget = SprintController.RetargetTicketsCommand(
             self.env, sprint=name, retarget=retarget, author=req.authname)
         self.controller.process_command(cmd_retarget)
Ejemplo n.º 6
0
 def _do_save(self, req, name):
     """Saves the sprint with the given name"""
     params = self._prepare_params(req, sprint=name)
     cmd_save = SprintController.SaveSprintCommand(self.env, **params)
     self.controller.process_command(cmd_save)
     sprint, old_sprint = params['sprint'], params['old_sprint']
     if sprint and old_sprint and sprint != old_sprint:
         name = sprint
     return self.redirect(req, SprintDisplayView(self.env), name)
Ejemplo n.º 7
0
 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))
Ejemplo n.º 8
0
 def _load_sprint(self, sprint_name, native=False):
     sprint = None
     error_message = None
     if sprint_name == None:
         error_message = 'No sprint specified'
     else:
         try:
             # Charts are a layer below the Sprint so they must not import this
             # module globally to avoid recursive imports
             from agilo.scrum.sprint import SprintController
             cmd = SprintController.GetSprintCommand
             cmd_get_sprint = cmd(self.env, sprint=sprint_name)
             cmd_get_sprint.native = native
             sprint = SprintController(
                 self.env).process_command(cmd_get_sprint)
         except Exception, e:
             error_message = 'Sprint %s does not exist' % unicode(e)
             sprint = None
Ejemplo n.º 9
0
 def _prepare_links(self, req, name, milestone):
     """Prepares the links for the navigation bar"""
     list_sprints = SprintController.ListSprintsCommand(
         self.env, criteria={'milestone': name})
     sprints = self.controller.process_command(list_sprints)
     for i, s in enumerate(sprints):
         s = s['sprint']
         if s['name'] == name:
             if i > 0:
                 add_link(req, 'prev',
                          self.href(sprints[i - 1]['sprint']['name']))
             if i < len(sprints) - 1:
                 add_link(req, 'next',
                          self.href(sprints[i + 1]['sprint']['name']))
             break
     # add back link to Milestone
     if milestone:
         add_link(req, 'up', req.href.milestone(milestone), 'Milestone')
     prevnext_nav(req, 'Sprint', 'Milestone')
Ejemplo n.º 10
0
    def prepare_data(self, req, args, data=None):
        """Returns the sprint visualization data"""
        if not args:
            raise TracError("You should provide at least a sprint name: ",
                            args)
        # Assuming there is a sprint name
        name = args.get('name')

        if data is None:
            data = {}
        # avoid circular import
        from agilo.scrum.backlog.web_ui import NewBacklogView
        data.update({
            'may_edit_sprint':
            Action.SPRINT_EDIT in req.perm,
            # show a deletion confirmation if user wanted to delete
            'delete_confirmation':
            'delete' in req.args,
            'close_confirmation':
            'close' in req.args,
            'edit_form_action':
            req.href(SprintEditView.url, name, 'edit'),
            'confirm_form_action':
            req.href(SprintConfirmView.url, name, 'confirm'),
            'sprint_backlog_link':
            req.href(NewBacklogView.url, Key.SPRINT_BACKLOG, name)
        })
        list_sprints = \
            SprintController.ListSprintsCommand(self.env,
                                                criteria={'name': '!=%s' % name})
        data['sprints'] = [s.name for s in \
                           self.controller.process_command(list_sprints)]
        # Generate the date converter function for this timezone
        convert_date = view.generate_converter(req.tz)
        get_sprint = SprintController.GetSprintCommand(self.env, sprint=name)
        sprint = self.controller.process_command(get_sprint)
        # update the sprint dates
        sprint.start = convert_date(sprint.start)
        sprint.end = convert_date(sprint.end)
        # update the data with the Sprint info
        data.update({'sprint': sprint})
        # now get the sprint ticket statistics
        cmd_stats = SprintController.GetTicketsStatisticsCommand(self.env,
                                                                 sprint=name,
                                                                 totals=True)
        nr_new, nr_planned, nr_closed = self.controller.process_command(
            cmd_stats)
        data['planned_tickets'] = nr_new
        data['in_progres_tickets'] = nr_planned
        data['open_tickets'] = nr_new + nr_planned
        data['closed_tickets'] = nr_closed

        rm = AgiloRoadmapModule(self.env)
        data['sprint_stats'] = rm.build_sprint_story_statistics(req, name)

        sprint_dates = {
            'start': sprint.start,
            'end': sprint.end,
            'duration': sprint.duration
        }

        if sprint.is_closed:
            data['date_info'] = "Ended the %(end)s. Duration %(duration)s days (started the %(start)s)" % \
                                    sprint_dates
        elif sprint.is_currently_running:
            data['date_info'] = "Started the %(start)s, ends on the %(end)s." % \
                                    sprint_dates
        else:
            data['date_info'] = "Starts the %(start)s. Duration %(duration)s days, ends on the %(end)s" % \
                                    sprint_dates
        return data
Ejemplo n.º 11
0
 def _do_create(self, req, name):
     """Creates a sprint getting data from the post request"""
     params = self._prepare_params(req, name=name)
     cmd_create = SprintController.CreateSprintCommand(self.env, **params)
     self.controller.process_command(cmd_create)
     return self.redirect(req, SprintDisplayView(self.env), name)
Ejemplo n.º 12
0
 def _get_sprint(self, req, name):
     cmd_get = SprintController.GetSprintCommand(self.env, sprint=name)
     return self.controller.process_command(
         cmd_get, date_converter=view.generate_converter(req.tz))
Ejemplo n.º 13
0
 def __init__(self):
     """Initialize a Sprint Controller"""
     self.controller = SprintController(self.env)
Ejemplo n.º 14
0
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