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)
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 ]
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)
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)
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)
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
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)
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"
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"
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
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
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'
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'
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)]
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)
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]
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
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
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)
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, {}