Exemplo n.º 1
0
    def __init__(self):
        """Setup the special ticket type.

        Checks if the Type enum contains 'pull request' and otherwise
        adds it with priority -1. That should somehow mark it *special*
        when looked at in the admin panel.

        The same way, two new resolutions are added.
        """
        try:
            item = Type(self.env, 'pull request')
        except ResourceNotFound:
            item = Type(self.env)
            item.name = 'pull request'
            item.value = -1
            item.insert()

        for resolution in ('accepted', 'rejected'):
            try:
                item = Resolution(self.env, resolution)
            except ResourceNotFound:
                item = Resolution(self.env)
                item.name = resolution
                item.value = -1
                item.insert()
Exemplo n.º 2
0
    def _upgrade_db(self, db):
        """Each schema version should have its own upgrade module, named
        upgrades/dbN.py, where 'N' is the version number (int).
        """
        dbm = DatabaseManager(self.env)
        if dbm.needs_upgrade(db_default.version, db_default.name):
            if not dbm.get_database_version(db_default.name):
                dbm.create_tables(db_default.tables)
                dbm.set_database_version(db_default.version, db_default.name)
            else:
                dbm.upgrade(db_default.version, db_default.name,
                            'crashdump.upgrades')

        try:
            Type(name='crashdump', env=self.env)
        except ResourceNotFound:
            crashdump_ticket_type = Type(env=self.env)
            crashdump_ticket_type.name = 'crashdump'
            crashdump_ticket_type.description = 'crashdump'
            crashdump_ticket_type.insert()

        custom = self.config['ticket-custom']
        config_dirty = False
        if 'linked_crash' not in custom:
            custom.set('linked_crash', 'text')
            custom.set('linked_crash.label', 'Linked crash')
            config_dirty = True
        if config_dirty:
            self.config.save()
Exemplo n.º 3
0
 def test_ticket_type_update(self):
     tkttype = Type(self.env, 'task')
     self.assertEqual(tkttype.name, 'task')
     self.assertEqual(tkttype.value, '3')
     tkttype.name = 'foo'
     tkttype.update()
     Type(self.env, 'foo')
Exemplo n.º 4
0
 def _create_custom_ticket_type(self, type_name, field_names):
     custom_type = TicketType(self.env)
     custom_type.name = type_name
     custom_type.insert()
     
     config = AgiloConfig(self.env)
     config.change_option(type_name, field_names, section=AgiloConfig.AGILO_TYPES)
     config.reload()
     self.assert_true(type_name in config.get_available_types())
Exemplo n.º 5
0
    def _create_custom_ticket_type(self, type_name, field_names):
        custom_type = TicketType(self.env)
        custom_type.name = type_name
        custom_type.insert()

        config = AgiloConfig(self.env)
        config.change_option(type_name,
                             field_names,
                             section=AgiloConfig.AGILO_TYPES)
        config.reload()
        self.assert_true(type_name in config.get_available_types())
Exemplo n.º 6
0
 def __init__(self):
     # Initialize ListOption()s for each type.
     # This makes sure they are visible in IniAdmin, etc
     self.types = [t.name for t in Type.select(self.env)]
     for t in self.types:
         hidden_fields = ListOption('condfields', t,
                                    doc='Fields to hide for type "%s"' % t)
         setattr(self.__class__, '%s_fields' % t, hidden_fields)
Exemplo n.º 7
0
 def _get_work_elements(self):
     """ Returns ticket types that are taken into consideration 
     while counting milestone progress """
     ignore_types = set(self.scope_element) \
         | set(self.excluded_element)
     return [
         type.name for type in Type.select(self.env)
         if type.name not in ignore_types
     ]
Exemplo n.º 8
0
    def test_handles_types_with_dashes(self):
        from_type = 'with-dashes'
        to_type = 'bug'
        custom_type = TicketType(self.env)
        custom_type.name = from_type
        custom_type.insert()

        config = AgiloConfig(self.env)
        config.change_option(from_type, "", section=AgiloConfig.AGILO_TYPES)
        config.reload()
        self.assert_true(from_type in config.get_available_types())

        section = config.get_section(AgiloConfig.AGILO_LINKS)
        allowed_links = section.get_list('allow')
        allowed_links.append('%s-%s' % (from_type, to_type))
        section.change_option('allow', ', '.join(allowed_links), save=True)
        self.links_configuration = LinksConfiguration(self.env)
        self.assert_equals(self.links_configuration.get_alloweds(from_type)[0].dest_type, to_type)
Exemplo n.º 9
0
 def _burnup_info(self, milestone):
     mils = [milestone.name]+[m.name for m in milestone.kids]
     tkt_types = None
     if self.count_burndown_on!='quantity':        
         all_types = Type.select(self.env)
         tkt_types = [type.name for type in all_types 
             if type.name not in IttecoEvnSetup(self.env).scope_element]
     else:
         #the only way to count progress on the tickets quantity is to have 
         # the same set of tickets for scope and for progress calculations
         tkt_types = self._work_types()
     return self._get_job_done(mils, tkt_types)
Exemplo n.º 10
0
 def _milestone_scope(self, milestone):
     """ Returns numeric representation of the scope for given milestone """
     
     mils = [milestone.name]
     tkt_types = None
     if self.count_burndown_on=='quantity':        
         mils += [m.name for m in milestone.kids]
         tkt_types = self._work_types()
     else:
         all_types = Type.select(self.env)
         tkt_types = [type.name for type in all_types 
             if type.name in IttecoEvnSetup(self.env).scope_element]
        
     return self._calculate_scope(mils, tkt_types)
Exemplo n.º 11
0
 def _burnup_info(self, milestone):
     mils = [milestone.name] + [m.name for m in milestone.kids]
     tkt_types = None
     if self.count_burndown_on != 'quantity':
         all_types = Type.select(self.env)
         tkt_types = [
             type.name for type in all_types
             if type.name not in IttecoEvnSetup(self.env).scope_element
         ]
     else:
         #the only way to count progress on the tickets quantity is to have
         # the same set of tickets for scope and for progress calculations
         tkt_types = self._work_types()
     return self._get_job_done(mils, tkt_types)
Exemplo n.º 12
0
    def references_search(self, req, q, filters=[]):
        """ Returns list of possible references objects for given search parameters."""
        if not q or not filters:
            return []
        query = SearchModule(self.env)._get_search_terms(q)

        all_types = [ticket.name for ticket in Type.select(self.env)]       
        used_types = [t for t in all_types if filters]
        if used_types:
            filters.append('ticket')
        filtered_res =[]
        for source in self.search_sources:
            for href, title, date, author, excerpt in source.get_search_results(req, query, filters):
                self.env.log.debug('references_search-res=%s' % ((href, title, date, author, excerpt),))
                path = href.split('/')
                res = {
                    'href': href,
                    'date': date,
                    'author': author,
                    'excerpt': excerpt,
                    'title' : title,
                    'type': path[-2],
                    'id': path[-1]
                }
                if(res['type']=='ticket'):
                    #hack to avoid database access
                    self.env.log.debug('references_search-ticket=%s' % res)

                    match = re.match('<span .+?>(.*?)</span>:(.+?):(.*)', str(title))
                    if match:
                        ticket_type = match.group(2).strip()
                        self.env.log.debug('references_search-ticket-type=%s' % ticket_type)
                        if ticket_type not in filters:
                            continue
                        res.update(
                            {
                                'title' :to_unicode(match.group(3)),
                                'idx': '%02d' % all_types.index(ticket_type),
                                'subtype' : ticket_type
                            }
                        )
                else:
                    res['title'] = to_unicode('wiki: %s' % res['title'].split(':',2)[0])
                    res['idx']=99
                filtered_res.append(res)
        
        filtered_res.sort(key= lambda x: '%s %s' % (x['idx'], x['title']))
        return filtered_res
Exemplo n.º 13
0
    def _milestone_scope(self, milestone):
        """ Returns numeric representation of the scope for given milestone """

        mils = [milestone.name]
        tkt_types = None
        if self.count_burndown_on == 'quantity':
            mils += [m.name for m in milestone.kids]
            tkt_types = self._work_types()
        else:
            all_types = Type.select(self.env)
            tkt_types = [
                type.name for type in all_types
                if type.name in IttecoEvnSetup(self.env).scope_element
            ]

        return self._calculate_scope(mils, tkt_types)
Exemplo n.º 14
0
    def ticket(self, req):
        tkt_id = req.args.get("obj_id")
        if tkt_id:
            req.perm.require("TICKET_MODIFY", Resource("ticket", tkt_id))
        else:
            req.perm.require("TICKET_CREATE")

        descriptor = WhiteboardModule(self.env).get_new_ticket_descriptor(
            [type.name for type in Type.select(self.env)], tkt_id
        )

        data = {
            "structured_milestones": StructuredMilestone.select(self.env),
            "resolutions": [],  # val.name for val in Resolution.select(self.env)],
            "new_ticket_descriptor": descriptor,
            "action_controls": self._get_action_controls(req, descriptor["ticket"]),
        }
        return "itteco_ticket_edit.html", data, "text/html"
Exemplo n.º 15
0
    def milestone(self, req):
        mil_id = req.args.get("obj_id")
        if mil_id:
            req.perm.require("MILESTONE_MODIFY", Resource("milestone", mil_id))
        else:
            req.perm.require("MILESTONE_CREATE")

        milestone = StructuredMilestone(self.env, mil_id)
        descriptor = WhiteboardModule(self.env).get_new_ticket_descriptor(
            [type.name for type in Type.select(self.env)], milestone.ticket.id
        )

        data = {
            "structured_milestones": StructuredMilestone.select(self.env),
            "new_ticket_descriptor": descriptor,
            "milestone": milestone,
            "action_controls": self._get_action_controls(req, descriptor["ticket"]),
        }
        return "itteco_milestone_quick_edit.html", data, "text/html"
Exemplo n.º 16
0
    def query_tasks(self, req, context):
        milestone_name = self._resolve_milestone(context.get('milestone'), context.get('show_sub_mils'), context.get('show_completed'))
       
        all_tkt_types = set([ticket_type.name for ticket_type in Type.select(self.env)])
        scope_tkt_types = set([t for t in IttecoEvnSetup(self.env).scope_element])
        workitem_tkt_types = all_tkt_types - scope_tkt_types \
            - set([t for t in IttecoEvnSetup(self.env).excluded_element])
        self.env.log.debug('workitem_tkt_types ="%s"' % (workitem_tkt_types,))
        
        roots, ticket_ids = self._get_tickets_graph(req, milestone_name, (scope_tkt_types , workitem_tkt_types))
        self.env.log.debug('roots ="%s"' % (roots,))
        empty_scope_element  = {'summary': 'Not assigned to any story'}
        not_assigned_work_items, _ignore = self._get_tickets_graph(req, milestone_name, (workitem_tkt_types,))

        for ticket in not_assigned_work_items:
            if ticket['id'] not in ticket_ids:
                empty_scope_element.setdefault('references', []).append(ticket)
        roots.append(empty_scope_element)
        return roots
Exemplo n.º 17
0
    def _get_ticket_config(self):
        ticket_config = self.env.config['itteco-whiteboard-tickets-config']
        if self._old_ticket_config!=ticket_config:
            default_fields = ticket_config.getlist('default_fields')
            show_workflow = ticket_config.getbool('show_workflow')

            allowed_tkt_types = [ type.name for type in Type.select(self.env)]
            _ticket_type_config = {}
            for option in ticket_config:
                try:
                    tkt_type, prop = option.split('.',1)
                    if tkt_type and ( tkt_type in allowed_tkt_types or \
                        tkt_type[0]=='$'):
                        _ticket_type_config.setdefault(
                            tkt_type, 
                            {
                                'fields'   : default_fields,
                                'workflow' : show_workflow
                            }
                        )[prop] = ticket_config.get(option)
                except ValueError :
                    pass

            scope_types = IttecoEvnSetup(self.env).scope_element
            scope_element_field_name = self.scope_element_weight_field
            
            work_types = IttecoEvnSetup(self.env).work_element
            work_element_field_name = self.work_element_weight_field
            
            for type in allowed_tkt_types:
                if type not in _ticket_type_config:
                    _ticket_type_config[type]={'fields':default_fields, 'workflow' : show_workflow}
            
            for type in _ticket_type_config.iterkeys():
                _ticket_type_config[type]['weight_field_name'] = \
                    type in scope_types and scope_element_field_name or work_element_field_name
                _ticket_type_config[type]['fields']=self._get_ticket_fields(
                    _ticket_type_config[type].get('fields'), [])
                    
            self._ticket_type_config = _ticket_type_config
            self._old_ticket_config=ticket_config
        return self._ticket_type_config
Exemplo n.º 18
0
    def ticket(self, req):
        tkt_id = req.args.get('obj_id')
        if tkt_id:
            req.perm.require('TICKET_MODIFY', Resource('ticket', tkt_id))
        else:
            req.perm.require('TICKET_CREATE')

        descriptor = WhiteboardModule(self.env).get_new_ticket_descriptor(
            [type.name for type in Type.select(self.env)], tkt_id)

        data = {
            'structured_milestones':
            StructuredMilestone.select(self.env),
            'resolutions':
            [],  #val.name for val in Resolution.select(self.env)],
            'new_ticket_descriptor':
            descriptor,
            'action_controls':
            self._get_action_controls(req, descriptor['ticket']),
        }
        return 'itteco_ticket_edit.html', data, 'text/html'
Exemplo n.º 19
0
    def milestone(self, req):
        mil_id = req.args.get('obj_id')
        if mil_id:
            req.perm.require('MILESTONE_MODIFY', Resource('milestone', mil_id))
        else:
            req.perm.require('MILESTONE_CREATE')

        milestone = StructuredMilestone(self.env, mil_id)
        descriptor = WhiteboardModule(self.env).get_new_ticket_descriptor(
            [type.name for type in Type.select(self.env)], milestone.ticket.id)

        data = {
            'structured_milestones':
            StructuredMilestone.select(self.env),
            'new_ticket_descriptor':
            descriptor,
            'milestone':
            milestone,
            'action_controls':
            self._get_action_controls(req, descriptor['ticket']),
        }
        return 'itteco_milestone_quick_edit.html', data, 'text/html'
Exemplo n.º 20
0
    def process_request(self, req):
        milestone_realm = Resource('milestone')
        req.perm.require('MILESTONE_VIEW')

        showall = req.args.get('show') == 'all'
        db = self.env.get_db_cnx()
        milestones = [m for m in StructuredMilestone.select(self.env, True, db)
                        if 'MILESTONE_VIEW' in req.perm(m.resource)]
        requested_fmt = req.args.get('format')
        if requested_fmt == 'ics':
            self.render_ics(req, db, milestones)
            return
        max_level = len(IttecoEvnSetup(self.env).milestone_levels)
        max_level = max_level and max_level-1 or 0;
        current_level = int(req.args.get('mil_type', max_level))
        
        if current_level==-1:
            #show all milestones regardless to the level
            milestones = sum([_get_milestone_with_all_kids(mil) for mil in milestones], [])
        else:
            #filter by level
            i =0        
            while i<current_level:
                next_level_mils = []
                for m in milestones:
                    next_level_mils.extend(m.kids)
                milestones = next_level_mils
                i+=1

        calc_on = req.args.get('calc_on')
        ticket_group = req.args.get('ticket_group', 'all')
        selected_types = None
        if ticket_group=='scope_element':
            selected_types = IttecoEvnSetup(self.env).scope_element
        elif ticket_group=='work_element':
            selected_types = IttecoEvnSetup(self.env).work_element
        else:
            ticket_group = 'all'
            
        selected_type_names = [tkt_type.name for tkt_type in Type.select(self.env) 
            if selected_types is None or tkt_type.value in selected_types]

        stats = []
        milestones = [mil for mil in milestones if showall or not mil.is_completed]
        for milestone in milestones:
            tickets = get_tickets_for_structured_milestone(
                self.env, db, milestone.name, calc_on, selected_type_names)
            tickets = apply_ticket_permissions(self.env, req, tickets)
            stat = SelectionTicketGroupStatsProvider(self.env).get_ticket_group_stats(tickets, calc_on)
            stats.append(
                milestone_stats_data(
                    req, stat, [m.name for m in _get_milestone_with_all_kids(milestone)]))

        if requested_fmt=='json':
            self._render_milestones_stats_as_json(req, milestones, stats)
            return
        # FIXME should use the 'webcal:' scheme, probably
        username = None
        if req.authname and req.authname != 'anonymous':
            username = req.authname
        icshref = req.href.roadmap(show=req.args.get('show'), user=username,
                                   format='ics')
        add_link(req, 'alternate', icshref, _('iCalendar'), 'text/calendar',
                 'ics')
        visibility = [{'index':idx, 'label': label, 'active': idx==current_level} 
            for idx, label in enumerate(IttecoEvnSetup(self.env).milestone_levels)]
        ticket_groups = [{'index':value, 'label': name, 'active': value==ticket_group} 
                for value, name in self._ticket_groups]
        
        calculate_on = self.get_statistics_source(req.args.get('calc_on'))
        data = {
            'milestones': milestones,
            'milestone_stats': stats,
            'mil_types': visibility,
            'ticket_groups': ticket_groups,
            'calc_on': calculate_on,
            'queries': [],
            'showall': showall,
        }
        self.env.log.debug('data:%s' % data)
        return 'itteco_roadmap.html', data, None
Exemplo n.º 21
0
 def __init__(self):
     # The following initialisations must happen inside init()
     # in order to be able to access self.env
     for tt in TicketType.select(self.env):
         self._add_per_ticket_type_option(tt.name)
Exemplo n.º 22
0
 def _add_ticket_types (self, req, ul):
     for t in Type.select(self.env):
         label = t.name
         href = '/newticket?type=' + label
         self._add_item(req, ul, label, href)
Exemplo n.º 23
0
 def get_scheduled_types(self):
     types = Type.select(self.env)
     config = self.config['ticket-relation-schedule']
     return [t.name for t in types if config.getbool(t.name + '.show_schedule', False)]
Exemplo n.º 24
0
    def render_admin_panel(self, req, cat, page, path_info):
        req.perm.assert_permission('TICKET_ADMIN')

        if req.method == 'POST':
            if req.args.get('add'):
                cur_types = self._get_all_types_with_workflow(True)
                name = req.args.get('name')
                if name.upper() in cur_types:
                    add_warning(req, _(
                        "There is already a workflow for ticket type '%s'. "
                        "Note that upper/lowercase is ignored"), name)
                else:
                    src_section = create_workflow_name(req.args.get('type'))
                    # Now copy the workflow
                    section = 'ticket-workflow-%s' % name
                    for key, val in self.config.options(src_section):
                        self.config.set(section, key, val)
                    self.config.save()
            elif req.args.get('remove'):
                workflow = 'ticket-workflow-%s' % req.args.get('sel')
                for key, val in self.config.options(workflow):
                    self.config.remove(workflow, key)
                self.config.save()
            elif req.args.get('save'):
                name = req.args.get('name', '')
                if name:
                    section = 'ticket-workflow-%s' % name
                else:
                    # If it's the default workflow the input is disabled and
                    # no value sent
                    section = 'ticket-workflow'

                # Change of workflow name. Remove old data from ini
                if name and name != path_info:
                    old_section = 'ticket-workflow-%s' % path_info
                    for key, val in self.config.options(old_section):
                        self.config.remove(old_section, key)

                # Save new workflow
                for key, val in self.config.options(section):
                    self.config.remove(section, key)
                for line in req.args.get('workflow-actions').split('\n'):
                    try:
                        key, val = line.split('=')
                        self.config.set(section, key, val)
                    except ValueError:
                        # Empty line or missing val
                        pass
                self.config.save()
            elif req.args.get('install'):
                self.install_workflow_controller(req)

            req.redirect(req.href.admin(cat, page))

        # GET, show admin page
        wf_controllers = self.config.getlist('ticket', 'workflow', [])
        data = {'types': self._get_all_types_with_workflow(),
                'trac_types': [enum.name for enum in Type.select(self.env)],
                'wf_controller_installed': u'MultipleWorkflowPlugin' in wf_controllers}
        if not path_info:
            data.update({'view': 'list', 'name': 'default'})
        else:
            data.update({'view': 'detail',
                         'name': path_info,
                         'workflowgraph': workflow_graph(self, req, path_info)})
            if path_info == 'default':
                data['workflow'] = ["%s = %s\n" % (key, val) for key, val in
                                    self.config.options('ticket-workflow')]
            else:
                data['workflow'] = ["%s = %s\n" % (key, val) for key, val in
                                    self.config.options('ticket-workflow-%s' %
                                                        path_info)]
            add_script(req, 'common/js/resizer.js')
            add_script_data(req, {
                'auto_preview_timeout': 2,
                'form_token': req.form_token,
                'trac_types': data['trac_types']})

        if self.pre_1_3:
            return 'multipleworkflowadmin.html', data
        else:
            return 'multipleworkflowadmin_jinja.html', data, {}
Exemplo n.º 25
0
 def _get_work_elements(self):
     """ Returns ticket types that are taken into consideration 
     while counting milestone progress """        
     ignore_types = set(self.scope_element) \
         | set(self.excluded_element) 
     return [type.name for type in Type.select(self.env) if type.name not in ignore_types]
Exemplo n.º 26
0
    def process_request(self, req):
        milestone_realm = Resource('milestone')
        req.perm.require('MILESTONE_VIEW')

        showall = req.args.get('show') == 'all'
        db = self.env.get_db_cnx()
        milestones = [
            m for m in StructuredMilestone.select(self.env, True, db)
            if 'MILESTONE_VIEW' in req.perm(m.resource)
        ]
        requested_fmt = req.args.get('format')
        if requested_fmt == 'ics':
            self.render_ics(req, db, milestones)
            return
        max_level = len(IttecoEvnSetup(self.env).milestone_levels)
        max_level = max_level and max_level - 1 or 0
        current_level = int(req.args.get('mil_type', max_level))

        if current_level == -1:
            #show all milestones regardless to the level
            milestones = sum(
                [_get_milestone_with_all_kids(mil) for mil in milestones], [])
        else:
            #filter by level
            i = 0
            while i < current_level:
                next_level_mils = []
                for m in milestones:
                    next_level_mils.extend(m.kids)
                milestones = next_level_mils
                i += 1

        calc_on = req.args.get('calc_on')
        ticket_group = req.args.get('ticket_group', 'all')
        selected_types = None
        if ticket_group == 'scope_element':
            selected_types = IttecoEvnSetup(self.env).scope_element
        elif ticket_group == 'work_element':
            selected_types = IttecoEvnSetup(self.env).work_element
        else:
            ticket_group = 'all'

        selected_type_names = [
            tkt_type.name for tkt_type in Type.select(self.env)
            if selected_types is None or tkt_type.value in selected_types
        ]

        stats = []
        milestones = [
            mil for mil in milestones if showall or not mil.is_completed
        ]
        for milestone in milestones:
            tickets = get_tickets_for_structured_milestone(
                self.env, db, milestone.name, calc_on, selected_type_names)
            tickets = apply_ticket_permissions(self.env, req, tickets)
            stat = SelectionTicketGroupStatsProvider(
                self.env).get_ticket_group_stats(tickets, calc_on)
            stats.append(
                milestone_stats_data(
                    req, stat,
                    [m.name for m in _get_milestone_with_all_kids(milestone)]))

        if requested_fmt == 'json':
            self._render_milestones_stats_as_json(req, milestones, stats)
            return
        # FIXME should use the 'webcal:' scheme, probably
        username = None
        if req.authname and req.authname != 'anonymous':
            username = req.authname
        icshref = req.href.roadmap(show=req.args.get('show'),
                                   user=username,
                                   format='ics')
        add_link(req, 'alternate', icshref, _('iCalendar'), 'text/calendar',
                 'ics')
        visibility = [{
            'index': idx,
            'label': label,
            'active': idx == current_level
        } for idx, label in enumerate(
            IttecoEvnSetup(self.env).milestone_levels)]
        ticket_groups = [{
            'index': value,
            'label': name,
            'active': value == ticket_group
        } for value, name in self._ticket_groups]

        calculate_on = self.get_statistics_source(req.args.get('calc_on'))
        data = {
            'milestones': milestones,
            'milestone_stats': stats,
            'mil_types': visibility,
            'ticket_groups': ticket_groups,
            'calc_on': calculate_on,
            'queries': [],
            'showall': showall,
        }
        self.env.log.debug('data:%s' % data)
        return 'itteco_roadmap.html', data, None