Пример #1
0
    def test_get_ticket_fields_milestone_update_completed(self):
        """Cached ticket fields are updated when milestone is completed
        date is changed.
        """
        fields = self.ticket_system.get_ticket_fields()
        milestone_field = self._get_ticket_field('milestone')
        m2 = Milestone(self.env, 'milestone2')
        m2.completed = datetime_now(utc)

        m2.update()
        updated_fields = self.ticket_system.get_ticket_fields()
        updated_milestone_field = self._get_ticket_field('milestone')

        self.assertNotEqual(fields, updated_fields)
        self.assertEqual(
            ['milestone1', 'milestone2', 'milestone3', 'milestone4'],
            milestone_field['options'])
        self.assertEqual(
            ['milestone2', 'milestone1', 'milestone3', 'milestone4'],
            updated_milestone_field['options'])
Пример #2
0
    def test_change_milestone_requires_milestone_view(self):
        """Changing ticket milestone requires MILESTONE_VIEW."""
        perm_sys = PermissionSystem(self.env)
        self._insert_ticket(summary='the summary')
        for name in ('milestone1', 'milestone2'):
            m = Milestone(self.env)
            m.name = name
            m.insert()

        def make_req(authname):
            return MockRequest(self.env,
                               authname=authname,
                               method='GET',
                               path_info='/ticket/1')

        def get_milestone_field(fields):
            for field in fields:
                if 'milestone' == field['name']:
                    return field

        perm_sys.grant_permission('user', 'TICKET_VIEW')
        req = make_req('user')

        self.assertTrue(self.ticket_module.match_request(req))
        data = self.ticket_module.process_request(req)[1]
        milestone_field = get_milestone_field(data['fields'])
        self.assertFalse(milestone_field['editable'])
        self.assertEqual([], milestone_field['optgroups'][0]['options'])
        self.assertEqual([], milestone_field['optgroups'][1]['options'])

        perm_sys.grant_permission('user_w_mv', 'TICKET_VIEW')
        perm_sys.grant_permission('user_w_mv', 'MILESTONE_VIEW')
        req = make_req('user_w_mv')

        self.assertTrue(self.ticket_module.match_request(req))
        data = self.ticket_module.process_request(req)[1]
        milestone_field = get_milestone_field(data['fields'])
        self.assertTrue(milestone_field['editable'])
        self.assertEqual([], milestone_field['optgroups'][0]['options'])
        self.assertEqual(['milestone1', 'milestone2'],
                         milestone_field['optgroups'][1]['options'])
Пример #3
0
 def _render_link(self, context, name, label, extra=''):
     if not (name or extra):
         return tag()
     try:
         milestone = Milestone(self.env, name)
     except ResourceNotFound:
         milestone = None
     # Note: the above should really not be needed, `Milestone.exists`
     # should simply be false if the milestone doesn't exist in the db
     # (related to #4130)
     href = context.href.milestone(name)
     exists = milestone and milestone.exists
     if exists:
         if 'MILESTONE_VIEW' in context.perm(milestone.resource):
             title = None
             if hasattr(context, 'req'):
                 if milestone.is_completed:
                     title = _(
                         "Completed %(duration)s ago (%(date)s)",
                         duration=pretty_timedelta(milestone.completed),
                         date=user_time(context.req, format_datetime,
                                        milestone.completed))
                 elif milestone.is_late:
                     title = _("%(duration)s late (%(date)s)",
                               duration=pretty_timedelta(milestone.due),
                               date=user_time(context.req, format_datetime,
                                              milestone.due))
                 elif milestone.due:
                     title = _("Due in %(duration)s (%(date)s)",
                               duration=pretty_timedelta(milestone.due),
                               date=user_time(context.req, format_datetime,
                                              milestone.due))
                 else:
                     title = _("No date set")
             closed = 'closed ' if milestone.is_completed else ''
             return tag.a(label, class_='%smilestone' % closed,
                          href=href + extra, title=title)
     elif 'MILESTONE_CREATE' in context.perm(self.realm, name):
         return tag.a(label, class_='missing milestone', href=href + extra,
                      rel='nofollow')
     return tag.a(label, class_=classes('milestone', missing=not exists))
Пример #4
0
 def render_ticket_action_control(self, req, ticket, action):
     actions = ConfigurableTicketWorkflow(self.env).actions
     label = actions[action]['label']
     hint = None
     old_milestone = ticket._old.get('milestone')
     if old_milestone is None:
         resolutions, milestone = \
             self._get_resolutions_and_milestone(action)
         if resolutions:
             try:
                 Milestone(self.env, milestone)
             except ResourceNotFound:
                 pass
             else:
                 res_hint = ', '.join("'%s'" % r for r in resolutions)
                 hint = _(
                     "For resolution %(resolutions)s the milestone "
                     "will be set to '%(milestone)s'.",
                     resolutions=res_hint,
                     milestone=milestone)
     return label, None, hint
Пример #5
0
 def task(add):
     """the thread task - either we are discovering or adding events"""
     with lock:
         env = ProductEnvironment(self.global_env, self.default_product)
         if add:
             name = 'milestone_from_' + threading.current_thread().name
             milestone = Milestone(env)
             milestone.name = name
             milestone.insert()
         else:
             # collect the names of milestones reported by Milestone and
             # directly from the db - as sets to ease comparison later
             results.append({
                 'from_t':
                 set([m.name for m in Milestone.select(env)]),
                 'from_db':
                 set([
                     v[0] for v in self.env.db_query(
                         "SELECT name FROM milestone")
                 ])
             })
Пример #6
0
    def process_request(self, req):

        req.perm.require('MILESTONE_VIEW')

        milestone_id = req.args.get('id')

        self.env.log.info("mdashboard process request %s, %s" %
                          (req.path_info, req.args.get('id')))

        add_link(req, 'up', req.href.pdashboard(), 'Dashboard')

        db = self.env.get_db_cnx()
        milestone = Milestone(self.env, milestone_id, db)

        if not milestone_id:
            req.redirect(req.href.pdashboard())

        self.env.log.info("request mdashboard")
        add_stylesheet(req, 'pd/css/dashboard.css')

        return self._render_view(req, db, milestone)
Пример #7
0
 def _render_link(self, context, name, label, extra=''):
     try:
         milestone = Milestone(self.env, name)
     except TracError:
         milestone = None
     # Note: the above should really not be needed, `Milestone.exists`
     # should simply be false if the milestone doesn't exist in the db
     # (related to #4130)
     href = context.href.milestone(name)
     if milestone and milestone.exists:
         if 'MILESTONE_VIEW' in context.perm(milestone.resource):
             closed = 'closed ' if milestone.is_completed else ''
             return tag.a(label,
                          class_='%smilestone' % closed,
                          href=href + extra)
     elif 'MILESTONE_CREATE' in context.perm('milestone', name):
         return tag.a(label,
                      class_='missing milestone',
                      href=href + extra,
                      rel='nofollow')
     return tag.a(label, class_='missing milestone')
Пример #8
0
def copy_milestone(source_env, dest_env, name, dest_db=None):
    # In case a string gets passed in
    if not isinstance(source_env, Environment):
        source_env = _open_environment(source_env)
    if not isinstance(dest_env, Environment):
        dest_env = _open_environment(dest_env)

    # Log message
    source_env.log.info(
        'DatamoverPlugin: Moving milestone %s to the environment at %s', name,
        dest_env.path)
    dest_env.log.info(
        'DatamoverPlugin: Moving milestone %s from the environment at %s',
        name, source_env.path)

    # Open databases
    source_db = source_env.get_db_cnx()
    source_cursor = source_db.cursor()
    handle_commit = True
    if not dest_db:
        dest_db, handle_commit = dest_env.get_db_cnx(), False
    dest_cursor = dest_db.cursor()

    # Remove the milestone from the destination
    try:
        dest_milestone = Milestone(dest_env, name, db=dest_db)
        dest_milestone.delete(retarget_to=name, db=dest_db)
    except TracError:
        pass

    # Copy each entry in the milestone table
    source_cursor.execute('SELECT * FROM milestone WHERE name=%s', (name, ))
    for row in source_cursor:
        milestone_data = dict(
            zip([d[0] for d in source_cursor.description], row))
        q = make_query(milestone_data, 'milestone')
        dest_cursor.execute(*q)

    if handle_commit:
        dest_db.commit()
Пример #9
0
    def test_add_milestone_with_spaces(self):
        name = 'mile stone 5'
        map = MilestoneAdminPanel(self.env)
        req = MockRequest(self.env,
                          method='POST',
                          args={
                              'name': ' mile \t stone \t 5 ',
                              'add': True
                          })

        self.assertRaises(ResourceNotFound, Milestone, self.env, name)
        self.assertRaises(RequestDone, map.render_admin_panel, req, 'ticket',
                          'milestone', None)
        self.assertIn('The milestone "mile stone 5" has been added.',
                      req.chrome['notices'])
        milestone = Milestone(self.env, name)
        self.assertEqual(name, milestone.name)

        with self.assertRaises(ResourceExistsError) as cm:
            map.render_admin_panel(req, 'ticket', 'milestone', None)
        self.assertIn('Milestone "mile stone 5" already exists',
                      unicode(cm.exception))
Пример #10
0
 def get_ticket_changes(self, req, ticket, action):
     if action == 'resolve' and \
             req.args and 'action_resolve_resolve_resolution' in req.args:
         old_milestone = ticket._old.get('milestone') or None
         user_milestone = ticket['milestone'] or None
         # If there's no user defined milestone, we try to set it
         # using the defined resolution -> milestone mapping.
         if old_milestone is None:
             new_status = req.args['action_resolve_resolve_resolution']
             new_milestone = self.__get_new_milestone(ticket, action,
                                                      new_status)
             # ... but we don't reset it to None unless it was None
             if new_milestone is not None or user_milestone is None:
                 try:
                     milestone = Milestone(self.env, new_milestone)
                     self.log.info('changed milestone from %s to %s',
                                   old_milestone, new_milestone)
                     return {'milestone': new_milestone}
                 except ResourceNotFound:
                     add_warning(req, _("Milestone %(name)s does not exist.",
                                        name=new_milestone))
     return {}
Пример #11
0
 def render_ticket_action_control(self, req, ticket, action):
     actions = ConfigurableTicketWorkflow(self.env).actions
     label = actions[action]['label']
     res_ms = self.__get_resolution_milestone_dict(ticket, action)
     resolutions = ''
     milestone = None
     for i, resolution in enumerate(res_ms):
         if i > 0:
             resolutions = "%s, '%s'" % (resolutions, resolution)
         else:
             resolutions = "'%s'" % resolution
             milestone = res_ms[resolution]
     hint = None
     if res_ms:
         try:
             Milestone(self.env, milestone)
         except ResourceNotFound:
             pass
         else:
             hint = _("For resolution %(resolutions)s the milestone will "
                      "be set to '%(milestone)s'.",
                      resolutions=resolutions, milestone=milestone)
     return label, None, hint
    def test_reset_milestone(self):
        self.env.config.set(
            'ticket', 'workflow',
            'ConfigurableTicketWorkflow,TicketWorkflowOpResetMilestone')
        self._config_set('ticket-workflow', [
            ('reset-milestone', '* -> *'),
            ('reset-milestone.operations', 'reset_milestone'),
        ])
        tktid = self._insert_ticket(when=datetime(2017, 3, 9, tzinfo=utc),
                                    summary='reset milestone',
                                    milestone='milestone1',
                                    reporter='anonymous',
                                    owner='joe')

        ticket = Ticket(self.env, tktid)
        req = self._post_req('reset-milestone', ticket)
        self.assertTrue(self.tktmod.match_request(req))
        self.assertRaises(RequestDone, self.tktmod.process_request, req)
        ticket = Ticket(self.env, tktid)
        self.assertEqual('milestone1', ticket['milestone'])

        milestone = Milestone(self.env, ticket['milestone'])
        milestone.completed = datetime(2017, 3, 8, tzinfo=utc)
        milestone.update()
        req = self._post_req('reset-milestone', ticket)
        self.assertTrue(self.tktmod.match_request(req))
        self.assertRaises(RequestDone, self.tktmod.process_request, req)
        ticket = Ticket(self.env, tktid)
        self.assertEqual('', ticket['milestone'])

        ticket['milestone'] = 'unknown-milestone'
        ticket.save_changes(when=datetime(2017, 3, 8, 1, tzinfo=utc))
        req = self._post_req('reset-milestone', ticket)
        self.assertTrue(self.tktmod.match_request(req))
        self.assertRaises(RequestDone, self.tktmod.process_request, req)
        ticket = Ticket(self.env, tktid)
        self.assertEqual('unknown-milestone', ticket['milestone'])
Пример #13
0
 def test_create_milestone_without_name(self):
     milestone = Milestone(self.env)
     self.assertRaises(AssertionError, milestone.insert)
Пример #14
0
Файл: model.py Проект: t2y/trac
 def _create_milestone(self, **values):
     milestone = Milestone(self.env)
     for k, v in values.iteritems():
         setattr(milestone, k, v)
     return milestone
Пример #15
0
Файл: model.py Проект: t2y/trac
    def test_update_milestone_without_name(self):
        self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')")

        milestone = Milestone(self.env, 'Test')
        milestone.name = None
        self.assertRaises(TracError, milestone.update)
Пример #16
0
 def _test_change_milestone(self, editor):
     milestone = Milestone(self.env)
     milestone.name = 'milestone1'
     milestone.insert()
     perm_cache = PermissionCache(self.env, editor, milestone.resource)
     return perm_cache, milestone.resource
Пример #17
0
 def testBacklogForMultipleSprint(self):
     """Tests a Backlog associated to a Sprint with multiple sprints"""
     # Creates a Milestone
     m = Milestone(self.env)
     m.name = "Milestone 1"
     m.insert()
     # Create 2 Sprints
     sprint1 = self.teh.create_sprint(name="Sprint 1",
                                      start=to_datetime(t=None),
                                      duration=20,
                                      milestone=m.name)
     sprint2 = self.teh.create_sprint(name="Sprint 2",
                                      start=to_datetime(t=None),
                                      duration=20,
                                      milestone=m.name)
     # Create some tickets
     s1 = self.teh.create_ticket(Type.USER_STORY,
                                 props={
                                     Key.STORY_POINTS: '3',
                                     Key.SPRINT: sprint1.name
                                 })
     self.assert_true(
         s1.link_to(
             self.teh.create_ticket(Type.TASK,
                                    props={
                                        Key.REMAINING_TIME: '4',
                                        Key.SPRINT: sprint1.name
                                    })))
     self.assert_true(
         s1.link_to(
             self.teh.create_ticket(Type.TASK,
                                    props={
                                        Key.REMAINING_TIME: '8',
                                        Key.SPRINT: sprint1.name
                                    })))
     self.assert_true(
         s1.link_to(
             self.teh.create_ticket(Type.TASK,
                                    props={Key.REMAINING_TIME: '4'})))
     s2 = self.teh.create_ticket(Type.USER_STORY,
                                 props={
                                     Key.STORY_POINTS: '5',
                                     Key.SPRINT: sprint2.name
                                 })
     self.assert_true(
         s2.link_to(
             self.teh.create_ticket(Type.TASK,
                                    props={
                                        Key.REMAINING_TIME: '2',
                                        Key.SPRINT: sprint2.name
                                    })))
     self.assert_true(
         s2.link_to(
             self.teh.create_ticket(Type.TASK,
                                    props={Key.REMAINING_TIME: '3'})))
     # Creates the Backlog bound to the Sprint
     backlog = BacklogConfiguration(self.env,
                                    name="Sprint-Backlog",
                                    type=BacklogType.SPRINT)
     backlog.ticket_types = [Type.USER_STORY, Type.TASK]
     backlog.save()
     # The Backlog should contains only the items planned for the Sprint, and with parents
     # planned for the Sprint too
     backlog1 = self.bmm.get(name="Sprint-Backlog", scope=sprint1.name)
     self.assert_length(3, backlog1)
     backlog2 = self.bmm.get(name="Sprint-Backlog", scope=sprint2.name)
     self.assert_length(2, backlog2)
Пример #18
0
    def _do_save(self, req, milestone):
        if milestone.exists:
            req.perm(milestone.resource).require('MILESTONE_MODIFY')
        else:
            req.perm(milestone.resource).require('MILESTONE_CREATE')

        old_name = milestone.name
        new_name = req.args.get('name')

        milestone.description = req.args.get('description', '')

        if 'due' in req.args:
            due = req.args.get('duedate', '')
            milestone.due = user_time(req, parse_date, due, hint='datetime') \
                            if due else None
        else:
            milestone.due = None

        completed = req.args.get('completeddate', '')
        retarget_to = req.args.get('target')

        # Instead of raising one single error, check all the constraints and
        # let the user fix them by going back to edit mode showing the warnings
        warnings = []

        def warn(msg):
            add_warning(req, msg)
            warnings.append(msg)

        # -- check the name
        # If the name has changed, check that the milestone doesn't already
        # exist
        # FIXME: the whole .exists business needs to be clarified
        #        (#4130) and should behave like a WikiPage does in
        #        this respect.
        try:
            new_milestone = Milestone(self.env, new_name)
            if new_milestone.name == old_name:
                pass  # Creation or no name change
            elif new_milestone.name:
                warn(
                    _(
                        'Milestone "%(name)s" already exists, please '
                        'choose another name.',
                        name=new_milestone.name))
            else:
                warn(_('You must provide a name for the milestone.'))
        except ResourceNotFound:
            milestone.name = new_name

        # -- check completed date
        if 'completed' in req.args:
            completed = user_time(req, parse_date, completed,
                                  hint='datetime') if completed else None
            if completed and completed > datetime.now(utc):
                warn(_('Completion date may not be in the future'))
        else:
            completed = None
        milestone.completed = completed

        if warnings:
            return self._render_editor(req, milestone)

        # -- actually save changes
        if milestone.exists:
            milestone.update()
            # eventually retarget opened tickets associated with the milestone
            if 'retarget' in req.args and completed:
                self.env.db_transaction(
                    """
                    UPDATE ticket SET milestone=%s
                    WHERE milestone=%s and status != 'closed'
                    """, (retarget_to, old_name))
                self.log.info("Tickets associated with milestone %s "
                              "retargeted to %s" % (old_name, retarget_to))
        else:
            milestone.insert()

        add_notice(req, _("Your changes have been saved."))
        req.redirect(req.href.milestone(milestone.name))
Пример #19
0
    def save_milestone(self, req, milestone):
        # Instead of raising one single error, check all the constraints
        # and let the user fix them by going back to edit mode and showing
        # the warnings
        warnings = []

        def warn(msg):
            add_warning(req, msg)
            warnings.append(msg)

        milestone.description = req.args.get('description', '')

        if 'due' in req.args:
            duedate = req.args.get('duedate')
            milestone.due = user_time(req, parse_date, duedate,
                                      hint='datetime') \
                            if duedate else None
        else:
            milestone.due = None

        # -- check completed date
        if 'completed' in req.args:
            completed = req.args.get('completeddate', '')
            completed = user_time(req, parse_date, completed,
                                  hint='datetime') if completed else None
            if completed and completed > datetime_now(utc):
                warn(_("Completion date may not be in the future"))
        else:
            completed = None
        milestone.completed = completed

        # -- check the name
        # If the name has changed, check that the milestone doesn't already
        # exist
        # FIXME: the whole .exists business needs to be clarified
        #        (#4130) and should behave like a WikiPage does in
        #        this respect.
        new_name = req.args.get('name')
        try:
            new_milestone = Milestone(self.env, new_name)
        except ResourceNotFound:
            milestone.name = new_name
        else:
            if new_milestone.name != milestone.name:
                if new_milestone.name:
                    warn(
                        _(
                            'Milestone "%(name)s" already exists, please '
                            'choose another name.',
                            name=new_milestone.name))
                else:
                    warn(_("You must provide a name for the milestone."))

        if warnings:
            return False

        # -- actually save changes
        if milestone.exists:
            milestone.update(author=req.authname)
            if completed and 'retarget' in req.args:
                comment = req.args.get('comment', '')
                retarget_to = req.args.get('target') or None
                retargeted_tickets = \
                    milestone.move_tickets(retarget_to, req.authname,
                                           comment, exclude_closed=True)
                add_notice(
                    req,
                    _(
                        'The open tickets associated with '
                        'milestone "%(name)s" have been retargeted '
                        'to milestone "%(retarget)s".',
                        name=milestone.name,
                        retarget=retarget_to))
                new_values = {'milestone': retarget_to}
                comment = comment or \
                          _("Open tickets retargeted after milestone closed")
                event = BatchTicketChangeEvent(retargeted_tickets, None,
                                               req.authname, comment,
                                               new_values, None)
                try:
                    NotificationSystem(self.env).notify(event)
                except Exception as e:
                    self.log.error(
                        "Failure sending notification on ticket "
                        "batch change: %s", exception_to_unicode(e))
                    add_warning(
                        req,
                        tag_(
                            "The changes have been saved, but "
                            "an error occurred while sending "
                            "notifications: %(message)s",
                            message=to_unicode(e)))
            add_notice(req, _("Your changes have been saved."))
        else:
            milestone.insert()
            add_notice(
                req,
                _('The milestone "%(name)s" has been added.',
                  name=milestone.name))

        return True
Пример #20
0
 def _get_milestone(self, name):
     try:
         return Milestone(self.env, name)
     except ResourceNotFound:
         return None
Пример #21
0
 def get_ticket_changes(self, req, ticket, action):
     """Returns the change of milestone, if needed."""
     milestone = Milestone(self.env,ticket['milestone'])
     if milestone.is_completed:
         return {'milestone': ''}
     return {}