Example #1
0
    def _get_wbitems_config(self, board_type):
        milestoneitem = {
            'realm' : 'milestone',
            'types' : [milestone_ticket_type],
            'weight': self.work_element_weight_field,
            'weightlabel' : 'CP'
        }
        scopeitem = {
            'realm' : 'ticket',
            'types' : [t for t in IttecoEvnSetup(self.env).scope_element],
            'weight': self.scope_element_weight_field,
            'weightlabel' : 'BV'
        }

        def read_options(fname):
            options = [ 
                f['options'] \
                for f in TicketSystem(self.env).get_ticket_fields() \
                if f['name']==fname
            ]
            if options:
                return options[0]
                
        workitem = {
            'realm' : 'ticket',
            'types' : [t for t in IttecoEvnSetup(self.env).work_element],
            'weight': self.work_element_weight_field,
            'weightlabel' : 'CP',
            'options' : read_options(self.work_element_weight_field)
        }
        
        if board_type=='team_tasks':
            return (scopeitem, workitem)
        else:
            return (milestoneitem, scopeitem)
Example #2
0
    def process_request(self, req):
        req.perm('ticket').require('TICKET_VIEW')
        
        board_type = req.args.get('board_type', 'team_tasks')
        milestone = req.args.get('milestone')
        if board_type == 'chart_settings':
            return self._chart_settings(milestone)
        else:
            board_type = _get_req_param(req, 'board_type', 'team_tasks')
            
            if board_type != req.args.get('board_type'):
                #boardtype was not implicitly  selected, let's restore previos state
                req.redirect(req.href.whiteboard(board_type, milestone))

            add_stylesheet(req, 'common/css/roadmap.css')
            add_stylesheet(req, 'itteco/css/common.css')            
            add_jscript(
                req, 
                [
                    'stuff/ui/ui.core.js',
                    'stuff/ui/ui.draggable.js',
                    'stuff/ui/ui.droppable.js',
                    'stuff/ui/ui.resizable.js',
                    'stuff/ui/plugins/jquery.colorbox.js',
                    'stuff/plugins/jquery.rpc.js',
                    'custom_select.js',
                    'whiteboard2.js'
                ],
                IttecoEvnSetup(self.env).debug
            )
            show_closed_milestones = req.args.get('show_closed_milestones', False)
            
            scope_item, work_item = self._get_wbitems_config(board_type)
            structured_milestones = StructuredMilestone.select(self.env, show_closed_milestones)
            if board_type == 'burndown':
                structured_milestones, _ignore = self._get_milestones_by_level(structured_milestones, 'Sprint', True)
            data ={
                'structured_milestones' : structured_milestones,
                'current_board_type' : board_type,
                'milestone' : milestone,
                'milestone_levels': IttecoEvnSetup(self.env).milestone_levels,
                'stats_config': self._get_stats_config(),
                'show_closed_milestones': show_closed_milestones,
                'wbconfig' : {
                    'rpcurl' : req.href.login("xmlrpc"),
                    'baseurl' : req.href(),
                    'workitem' : work_item,
                    'scopeitem': scope_item,
                    'groups': self.ticket_groups,
                    'transitions': self.transitions
                },
                'team' : self.team_members_provider and self.team_members_provider.get_team_members() or [],
                'ticket_types' : work_item['types'] or []
            }
                
            return 'itteco_whiteboard2.html', data, 'text/html'
Example #3
0
    def setUp(self):
        self.env = EnvironmentStub(enable=['trac.*', 'itteco.*'])
        self.env.config.set('trac', 'permission_policies',
                            'CalendarSystem, DefaultPermissionPolicy')

        self.itteco_env = IttecoEvnSetup(self.env)
        self.itteco_env.upgrade_environment(self.env.get_db_cnx())

        self.calendar_system = CalendarSystem(self.env)
        self.perm_system = perm.PermissionSystem(self.env)

        self.perm = perm.PermissionCache(self.env, 'testuser')
Example #4
0
class CalendarSystemTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub(enable=['trac.*', 'itteco.*'])
        self.env.config.set('trac', 'permission_policies',
                            'CalendarSystem, DefaultPermissionPolicy')

        self.itteco_env = IttecoEvnSetup(self.env)
        self.itteco_env.upgrade_environment(self.env.get_db_cnx())

        self.calendar_system = CalendarSystem(self.env)
        self.perm_system = perm.PermissionSystem(self.env)

        self.perm = perm.PermissionCache(self.env, 'testuser')

    def tearDown(self):
        self.env.reset_db()

    def _create_calendar(self, name, username, type=CalendarType.Private):
        calendar = Calendar(self.env, None, username)
        calendar.theme = 1
        calendar.type = type
        id = calendar.insert()
        return calendar

    def test_permission_policies(self):
        self.perm_system.grant_permission('testuser', 'CALENDAR_CREATE')
        self.perm_system.grant_permission('testuser', 'CALENDAR_MODIFY')
        self.perm_system.grant_permission('testuser', 'CALENDAR_VIEW')
        self.perm_system.grant_permission('testuser', 'CALENDAR_DELETE')

        self.assertTrue('CALENDAR_CREATE' in self.perm)

        calendar = self._create_calendar('cal1', 'testuser')
        c = calendar.resource

        self.assertTrue('CALENDAR_MODIFY' in self.perm(c))
        self.assertTrue('CALENDAR_VIEW' in self.perm(c))
        self.assertTrue('CALENDAR_DELETE' in self.perm(c))

        calendar2 = self._create_calendar('cal2', 'user1')
        c2 = calendar2.resource

        self.assertTrue('CALENDAR_VIEW' not in self.perm(c2), \
            'There should be no access to private calendar of another user.')

        calendar3 = self._create_calendar('cal3', 'user1', CalendarType.Shared)
        c3 = calendar3.resource

        self.assertTrue('CALENDAR_VIEW' in self.perm(c3), \
            'There should view access to shared calendars.')
Example #5
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
Example #6
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
Example #7
0
    def _get_job_done(self, mil_names, tkt_type=None, db=None):
        started_at = to_timestamp(datetime(tzinfo=localtz, \
            *(StructuredMilestone(self.env, mil_names[0]).started.timetuple()[:3])))
        db = db or self.env.get_db_cnx()
        cursor = db.cursor()

        base_sql = None
        params = list(mil_names)
        group_by = " GROUP BY t.id, t.type"
        final_statuses = IttecoEvnSetup(self.env).final_statuses
        status_params = ("%s," * len(final_statuses))[:-1]
        params = final_statuses + final_statuses + params
        if self.count_burndown_on == 'quantity':
            base_sql = """SELECT MAX(c.time), t.id, t.type, 1
                FROM ticket t 
                    LEFT JOIN milestone m ON m.name=t.milestone 
                    LEFT OUTER JOIN ticket_change c ON t.id=c.ticket 
                        AND c.field='status' AND c.newvalue IN (%s) 
                WHERE IN (%s) AND m.name IN (%s)""" % \
                    (
                        status_params,
                        status_params,
                        ("%s,"*len(mil_names))[:-1]
                    )
        else:
            base_sql = "SELECT MAX(c.time), t.id, t.type, "+db.cast(db.concat('0','tc.value'),'int')+ \
                """FROM ticket t 
                    LEFT JOIN milestone m ON m.name=t.milestone 
                    LEFT JOIN ticket_custom tc ON t.id=tc.ticket AND tc.name=%%s
                    LEFT OUTER JOIN ticket_change c ON t.id=c.ticket AND c.field='status' 
                        AND c.newvalue IN (%s) 
                WHERE t.status IN (%s) AND m.name IN (%s)""" % \
                    (
                        status_params,
                        status_params,
                        ("%s,"*len(mil_names))[:-1]
                    )
            params = [self.count_burndown_on] + params
            group_by += ", tc.value"
        if tkt_type:
            if isinstance(tkt_type, basestring):
                base_sql += " AND t.type=%s"
                params += [tkt_type]
            else:
                base_sql += " AND t.type IN (%s)" % ("%s," *
                                                     len(tkt_type))[:-1]
                params += list(tkt_type)

        cursor.execute(base_sql + group_by + " ORDER BY 1", params)
        data = [(to_datetime((dt < started_at) and started_at
                             or dt), ttype, sum or 0)
                for dt, tkt_id, ttype, sum in cursor]
        return data
Example #8
0
 def setUp(self):
     self.env = EnvironmentStub(enable=['trac.*', 'itteco.*'])
     self.env.config.set('trac', 'permission_policies', 'CalendarSystem, DefaultPermissionPolicy')
     
     self.itteco_env= IttecoEvnSetup(self.env)
     self.itteco_env.upgrade_environment(self.env.get_db_cnx())
     
     #self.calendar_system = CalendarSystem(self.env)
     self.rpc = CalendarRPC(self.env)
     self.perm_system = perm.PermissionSystem(self.env)        
     self.req = Mock()
     self.req.perm = perm.PermissionCache(self.env, 'testuser')
     self._createTestData();
Example #9
0
class CalendarRPCTestCase(unittest.TestCase):

    def setUp(self):
        self.env = EnvironmentStub(enable=['trac.*', 'itteco.*'])
        self.env.config.set('trac', 'permission_policies', 'CalendarSystem, DefaultPermissionPolicy')
        
        self.itteco_env= IttecoEvnSetup(self.env)
        self.itteco_env.upgrade_environment(self.env.get_db_cnx())
        
        #self.calendar_system = CalendarSystem(self.env)
        self.rpc = CalendarRPC(self.env)
        self.perm_system = perm.PermissionSystem(self.env)        
        self.req = Mock()
        self.req.perm = perm.PermissionCache(self.env, 'testuser')
        self._createTestData();


    def tearDown(self):
        self.env.reset_db()

    def _createTestData(self):
        self._create_calendar('cal1', 'testuser')
        self._create_calendar('cal2', 'testuser')
        self._create_calendar('cal3', 'user1')
        self._create_calendar('cal4', 'user1', CalendarType.Shared)
        
    def _create_calendar(self, name, username, type= CalendarType.Private):
        calendar = Calendar(self.env, None, username)
        calendar.name = name
        calendar.theme = 1
        calendar.type = type
        id = calendar.insert()
        return calendar
        

    def test_calendars_rpc(self):
        self.assertEqual('calendar', self.rpc.xmlrpc_namespace())
        self.assertEqual(['cal1','cal2','cal4'], [c.name for c in self.rpc.query(self.req)])
Example #10
0
    def delete(self, retarget_to=None, author=None, db=None):
        handle_commit = False
        if not db:
            db = self.env.get_db_cnx()
            handle_commit = True

        self.milestone.delete(retarget_to, author, db)
        self.ticket.delete(db)
        if handle_commit:
            db.commit()
            
        listeners = IttecoEvnSetup(self.env).change_listeners
        for listener in listeners:
            listener.milestone_deleted(self)
Example #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)
Example #12
0
class CalendarRPCTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub(enable=['trac.*', 'itteco.*'])
        self.env.config.set('trac', 'permission_policies',
                            'CalendarSystem, DefaultPermissionPolicy')

        self.itteco_env = IttecoEvnSetup(self.env)
        self.itteco_env.upgrade_environment(self.env.get_db_cnx())

        #self.calendar_system = CalendarSystem(self.env)
        self.rpc = CalendarRPC(self.env)
        self.perm_system = perm.PermissionSystem(self.env)
        self.req = Mock()
        self.req.perm = perm.PermissionCache(self.env, 'testuser')
        self._createTestData()

    def tearDown(self):
        self.env.reset_db()

    def _createTestData(self):
        self._create_calendar('cal1', 'testuser')
        self._create_calendar('cal2', 'testuser')
        self._create_calendar('cal3', 'user1')
        self._create_calendar('cal4', 'user1', CalendarType.Shared)

    def _create_calendar(self, name, username, type=CalendarType.Private):
        calendar = Calendar(self.env, None, username)
        calendar.name = name
        calendar.theme = 1
        calendar.type = type
        id = calendar.insert()
        return calendar

    def test_calendars_rpc(self):
        self.assertEqual('calendar', self.rpc.xmlrpc_namespace())
        self.assertEqual(['cal1', 'cal2', 'cal4'],
                         [c.name for c in self.rpc.query(self.req)])
Example #13
0
    def insert(self, db=None):
        assert self.name, 'Cannot create milestone with no name'
        handle_commit = False
        if not db:
            db = self.env.get_db_cnx()
            handle_commit = True

        self.milestone.insert(db)
        self.ticket.insert(db=db)
        if handle_commit:
            db.commit()
            
        listeners = IttecoEvnSetup(self.env).change_listeners
        for listener in listeners:
            listener.milestone_created(self)
Example #14
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)
Example #15
0
    def filter_stream(self, req, method, filename, stream, data):
        if req.path_info.startswith('/report/'):
            link_builder = req.href.chrome
            debug = IttecoEvnSetup(self.env).debug

            def script_tag(path=None, content=None):
                kw = {'type': "text/javascript"}
                if path:
                    kw['src'] = link_builder(map_script(path, debug))
                return tag.script(content, **kw)

            stream |= Transformer("//head").prepend(
                tag.link(type="text/css",
                         rel="stylesheet",
                         href=link_builder("itteco/css/report.css"))
            ).append(
                tag(
                    script_tag('stuff/plugins/jquery.rpc.js'),
                    script_tag('stuff/ui/plugins/jquery.jeditable.js'),
                    script_tag("editable_report.js"),
                    script_tag(
                        content="$(document).ready(function(){" +
                        "$('#main').editableReport(" + "{" + "rpcurl: '" +
                        req.href('login', 'xmlrpc') + "'," + "fields: " +
                        self.fields_dict(data.get('header_groups', [])) +
                        "})" + "});")))
            try:
                stream |= Transformer("//head").prepend(
                    tag(
                        # TODO fix scripts base path
                        tag.link(type="text/css",
                                 rel="stylesheet",
                                 href=link_builder("itteco/css/common.css")),
                        tag.link(type="text/css",
                                 rel="stylesheet",
                                 href=link_builder("itteco/css/report.css")),
                        #tag.link(type="text/css", rel="stylesheet", href=link_builder("itteco/css/colorbox/colorbox.css"))
                    )).append(
                        tag(script_tag("stuff/ui/ui.core.js"),
                            script_tag("stuff/ui/ui.draggable.js"),
                            script_tag("stuff/ui/ui.droppable.js"),
                            script_tag("stuff/ui/ui.resizable.js"),
                            script_tag('stuff/ui/plugins/jquery.colorbox.js'),
                            script_tag('custom_select.js')))
            except ValueError, e:
                #we do not fail the report it self, may be it works in read only mode
                self.env.log.debug('Report decoration failed: %s' % e)
Example #16
0
    def process_request(self, req):
        if req.method == 'POST' and ('preview'
                                     not in req.args) and req.args.get('id'):
            id = int(req.args.get('id'))
            ticket = Ticket(self.env, id)
            if ticket.exists:

                def get_ids(req, attr_name):
                    ids = req.args.get(attr_name, [])
                    return isinstance(ids, basestring) and (ids, ) or ids

                links = TicketLinks(self.env, ticket)
                links.outgoing_links = [
                    int(id) for id in get_ids(req, 'ticket_links')
                ]
                links.wiki_links = get_ids(req, 'wiki_links')
                links.save()
        template, data, content_type = req.args[
            'original_handler'].process_request(
                RedirectInterceptor(req, self._get_jump_to_url))
        if template == 'ticket.html':
            add_jscript(req, ['stuff/plugins/jquery.rpc.js', 'references.js'],
                        IttecoEvnSetup(self.env).debug)
            tkt = data['ticket']
            links = TicketLinks(self.env, tkt)
            data['filters'] = self._get_search_filters(req)
            data['ticket_links'] = {
                'incoming': {
                    'title': 'Referred by:',
                    'blockid': 'inblock',
                    'removable': False,
                    'links': self._ids_to_tickets(links.incoming_links)
                },
                'outgoing': {
                    'title': 'Refers to:',
                    'blockid': 'outblock',
                    'removable': True,
                    'links': self._ids_to_tickets(links.outgoing_links)
                },
                'wiki': links.wiki_links
            }

            data['jump_to'] = req.args.get('jump_to') or referer_module(req)

            return 'itteco_ticket.html', data, content_type
        return template, data, content_type
Example #17
0
 def save_changes(self, author, comment, when=None, db=None, cnum=''):
     assert self.name, 'Cannot update milestone with no name'
     handle_commit = False
     if not db:
         db = self.env.get_db_cnx()
         handle_commit = True
     old_values =  self.ticket._old
             
     self.milestone.update(db)
     self.ticket.save_changes(author, comment, when, db, cnum)
     
     if handle_commit:
         db.commit()
         
     old_values['name'] = self.name
     listeners = IttecoEvnSetup(self.env).change_listeners
     for listener in listeners:
         listener.milestone_changed(self, old_values)
Example #18
0
    def post_process_request(self, req, template, data, content_type):
        self.env.log.debug(
            'post_process_request req=%s, pathinfo=%s, args=%s' %
            (req, req.path_info, req.args))
        if req.path_info.startswith('/ticket/') \
            or req.path_info.startswith('/newticket') \
            or req.path_info.startswith('/milestone') \
            or req.path_info.startswith('/roadmap'):

            #add_stylesheet(req, 'itteco/css/common.css')
            add_jscript(req, [
                'stuff/ui/ui.core.js', 'stuff/ui/ui.resizable.js',
                'stuff/ui/ui.draggable.js', 'stuff/ui/ui.droppable.js',
                'custom_select.js'
            ],
                        IttecoEvnSetup(self.env).debug)

        return template, data, content_type
Example #19
0
    def my_active_tickets(self, req):
        """ Returns all none closed tickets assigned to the current user."""
        user = req.authname
        def ticket_as_dict(id, summary):
            return {
                'ticketId': id, 
                'summary': summary
            }
            
        final_statuses = [status for status in IttecoEvnSetup(self.env).final_statuses]

        db = self.env.get_db_cnx()
        cursor = db.cursor()
        cursor.execute("""
            SELECT id as ticketId, summary
              FROM ticket
             WHERE owner = %%s AND status NOT IN (%s)""" % ("%s," * len(final_statuses))[:-1],
           [user,]+final_statuses)
        return [ticket_as_dict(tktId, summary) for tktId, summary in cursor]
Example #20
0
    def _render_ui(self, req):
        #add_stylesheet(req, 'itteco/css/colorbox/colorbox.css')
        add_stylesheet(req, 'itteco/css/common.css')
        add_stylesheet(req, 'itteco/css/calendar.css')

        add_jscript(req, [
            'stuff/ui/ui.core.js', 'stuff/ui/ui.draggable.js',
            'stuff/ui/ui.droppable.js', 'stuff/ui/ui.resizable.js',
            'stuff/ui/ui.datepicker.js', 'stuff/ui/ui.slider.js',
            'stuff/ui/plugins/fullcalendar.js',
            'stuff/ui/plugins/jquery.colorbox.js',
            'stuff/ui/plugins/timepicker.js', 'stuff/plugins/jquery.rpc.js',
            'calendar.js'
        ],
                    IttecoEvnSetup(self.env).debug)

        icshref = req.href.calendar(format='ics')
        add_link(req, 'alternate', icshref, _('iCalendar'), 'text/calendar',
                 'ics')

        return 'itteco_calendar_view.html', {}, None
Example #21
0
    def _add_tickets_report_data(self, milestone, req, data):
        tickets_report = MilestoneSystem(self.env).tickets_report
        if tickets_report:
            req.args['MILESTONE'] = milestone.name
            req.args['id'] = tickets_report
            report_data = IttecoReportModule(self.env).get_report_data(
                req, tickets_report)
            self.env.log.debug('report_data="%s"' % (report_data, ))
            data.update(report_data)

            debug = IttecoEvnSetup(self.env).debug

            add_jscript(req, 'stuff/plugins/jquery.rpc.js', debug)
            add_jscript(req, 'stuff/ui/plugins/jquery.jeditable.js', debug)
            add_jscript(req, 'stuff/ui/ui.core.js', debug)
            add_jscript(req, 'stuff/ui/ui.draggable.js', debug)
            add_jscript(req, 'stuff/ui/ui.droppable.js', debug)
            add_jscript(req, 'editable_report.js', debug)
            add_jscript(req, 'report.js', debug)
            #add_stylesheet(req, 'itteco/css/report.css')
            data['fields_config'] = IttecoReportModule(self.env).fields_dict(
                data.get('header_groups', []))
        data['render_report'] = tickets_report or None
Example #22
0
    def query_stories(self, req, context):
        level = context.get('level')
        
        all_milestones = StructuredMilestone.select(self.env, True)
        mils, mils_dict = self._get_milestones_by_level(all_milestones, level, context.get('show_completed'))
        milestones = [mil.name for mil in mils] +['']
        fields = [
            'summary', 
            'description',
            'owner',
            self.scope_element_weight_field, 
            self.work_element_weight_field
        ]

        def milestone_as_dict(milestone):
            res = dict([(f, milestone.ticket[f]) for f in fields])
            res.update(
                {
                    'id': milestone.name,
                    'references': []
                }
            )
            return res
        empty_scope_element  = {'id': '', 'summary': 'Backlog (no milestone)','references': []}
        
        roots = [empty_scope_element] + [milestone_as_dict(m) for m in mils]
        milestone_by_name = dict([(m['id'], m) for m in roots])
       
        scope_tkt_types = set([t for t in IttecoEvnSetup(self.env).scope_element])       
        tickets, ticket_ids = self._get_tickets_graph(req, milestones, (scope_tkt_types,))
        
        self.env.log.debug('roots ="%s"' % (roots,))
        for ticket in tickets:
            root = milestone_by_name.get(ticket['milestone'],empty_scope_element)
            root['references'].append(ticket)
            
        return roots
Example #23
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
Example #24
0
 def _get_level_label(self, idx):
     label = 'undefined'
     levels = IttecoEvnSetup(self.env).milestone_levels
     if levels and idx < len(levels):
         label = levels[idx]
     return label
Example #25
0
    def _render_admin_panel(self, req, cat, page, milestone):
        req.perm.require('TICKET_ADMIN')
        add_stylesheet(req, 'itteco/css/common.css')
        add_jscript(
            req, 
            [
                'stuff/ui/ui.core.js',
                'stuff/ui/ui.resizable.js',
                'custom_select.js'
            ],
            IttecoEvnSetup(self.env).debug
        )
        # Detail view?
        if milestone:
            mil = StructuredMilestone(self.env, milestone)
            if req.method == 'POST':
                if req.args.get('save'):
                    mil.name = req.args.get('name')
                    mil.due = mil.completed = None
                    due = req.args.get('duedate', '')
                    if due:
                        mil.due = parse_date(due, req.tz)
                    if req.args.get('completed', False):
                        completed = req.args.get('completeddate', '')
                        mil.completed = parse_date(completed, req.tz)
                        if mil.completed > datetime.now(utc):
                            raise TracError(_('Completion date may not be in '
                                              'the future'),
                                            _('Invalid Completion Date'))
                    mil.description = req.args.get('description', '')
                    mil.parent = req.args.get('parent', None)
                    if mil.parent and mil.parent==mil.name:
                        raise TracError(_('Milestone cannot be parent for itself,Please, give it another thought.'),
                                        _('Something is wrong with Parent Milestone. Will you check it please?'))

                    if mil.parent and not StructuredMilestone(self.env, mil.parent).exists:
                        raise TracError(_('Milestone should have a valid parent. It does not look like this is the case.'),
                                        _('Something is wrong with Parent Milestone. Will you check it please?'))
                    mil.update()
                    req.redirect(req.href.admin(cat, page))
                elif req.args.get('cancel'):
                    req.redirect(req.href.admin(cat, page))

            add_script(req, 'common/js/wikitoolbar.js')
            data = {'view': 'detail', 'milestone': mil}

        else:
            if req.method == 'POST':
                # Add Milestone
                if req.args.get('add') and req.args.get('name'):
                    name = req.args.get('name')
                    try:
                        StructuredMilestone(self.env, name)
                    except ResourceNotFound:
                        mil = StructuredMilestone(self.env)
                        mil.name = name
                        if req.args.get('duedate'):
                            mil.due = parse_date(req.args.get('duedate'),
                                                 req.tz)
                        mil.parent = req.args.get('parent', None)
                        if mil.parent and not StructuredMilestone(self.env, mil.parent).exists:
                            raise TracError(_('Milestone should have a valid parent. It does not look like this is the case'),
                                            _('Something is wrong with Parent Milestone. Will you check it please?'))

                        mil.insert()
                        req.redirect(req.href.admin(cat, page))
                    else:
                        raise TracError(_('Sorry, milestone %s already exists.') % name)

                # Remove milestone
                elif req.args.get('remove'):
                    sel = req.args.get('sel')
                    if not sel:
                        raise TracError(_('Please, select the milestone.'))
                    if not isinstance(sel, list):
                        sel = [sel]
                    db = self.env.get_db_cnx()
                    for name in sel:
                        mil = StructuredMilestone(self.env, name, db=db)
                        mil.delete(db=db)
                    db.commit()
                    req.redirect(req.href.admin(cat, page))

                # Set default milestone
                elif req.args.get('apply'):
                    if req.args.get('default'):
                        name = req.args.get('default')
                        self.config.set('ticket', 'default_milestone', name)
                        self.config.save()
                        req.redirect(req.href.admin(cat, page))

            data = {
                'view': 'list',
                'default': self.config.get('ticket', 'default_milestone'),
            }
            
        # Get ticket count
        db = self.env.get_db_cnx()
        cursor = db.cursor()
        milestones = []
        structured_milestones = StructuredMilestone.select(self.env)
        mil_names = self._get_mil_names(structured_milestones)
        
        cursor.execute("SELECT milestone, COUNT(*) FROM ticket "
                   "WHERE milestone IN (%s) GROUP BY milestone" % ("%s,"*len(mil_names))[:-1], mil_names)
        mil_tkt_quantity = {}
        for mil, cnt in cursor:
            mil_tkt_quantity[mil]=cnt

        data.update({
            'date_hint': get_date_format_hint(),
            'milestones': [(mil, 0) for mil in structured_milestones],# we recover this anyway
            'structured_milestones': structured_milestones,
            'milestone_tickets_quantity': mil_tkt_quantity,
            'max_milestone_level': self.milestone_levels and len(self.milestone_levels)-1 or 0,
            'datetime_hint': get_datetime_format_hint()
        })
        return 'itteco_admin_milestones.html', data
Example #26
0
    def metrics(self, milestone):
        mil = StructuredMilestone(self.env, milestone)
        if not mil.is_started:
            return None

        scope = self._milestone_scope(mil)

        scope_types = IttecoEvnSetup(self.env).scope_element
        types = self._work_types()
        one_day_delta = timedelta(1)

        metrics = [{
            'datetime': mil.started,
            'burndown': scope,
            'burnup': [0] * len(types)
        }]

        def clone_item(item, new_time):
            item = deepcopy(item)
            item['datetime'] = new_time
            metrics.append(item)
            return item

        for ts, ttype, sum in self._burnup_info(mil):
            last = metrics[-1]
            if ts != last['datetime']:
                if self.fill_idle_days:
                    time_delta = ts - last['datetime']
                    if time_delta.days > 1:
                        last = clone_item(last, ts - one_day_delta)
                last = clone_item(last, ts)

            if ttype in self.initial_plan_element:
                #this ticket type should influence burndown
                last['burndown'] -= sum
            elif ttype not in scope_types:
                #burnup by ticket type turned around axis X, in order to look like burndown
                idx = types.index(ttype)
                last['burnup'][idx] += sum

        calculated_end_date = None
        if len(metrics) > 1:  # do we have any progress?
            start = metrics[0]
            end = metrics[-1]
            if start['burndown'] != end['burndown']:
                # do we have any progress to perform approximations?
                start_ts = to_timestamp(start['datetime'])
                end_ts = to_timestamp(end['datetime'])
                calc_ts = start_ts+ \
                    start['burndown']*(end_ts - start_ts)/ \
                        (start['burndown']-end['burndown'])
                end['approximation'] = end['burndown']
                calculated_end_date = to_datetime(calc_ts)

        if mil.due:  #if milestone is fully planned, add line of ideal progress
            if mil.due > metrics[-1]['datetime']:
                metrics.append({'datetime': mil.due})
            metrics[0]['ideal'] = scope
            metrics[-1]['ideal'] = 0

        if calculated_end_date:
            if calculated_end_date > metrics[-1]['datetime']:
                metrics.append({
                    'datetime': calculated_end_date,
                    'approximation': 0
                })
            else:
                #let's find a correct place on timeline for calculated end date
                for i in xrange(0, len(metrics)):
                    metric = metrics[i]
                    if metric['datetime'] >= calculated_end_date:
                        if metric['datetime'] > calculated_end_date:
                            metric = {'datetime': calculated_end_date}
                            metrics.insert(i, metric)
                        metric['approximation'] = 0
                        break

        return metrics, types
Example #27
0
 def _work_types(self):
     """ Returns ticket types that are taken into consideration 
     while counting milestone progress """
     return list(set(IttecoEvnSetup(self.env).work_element) - \
         set(self.initial_plan_element))