def _get_user_data_(self, req, milestone, field, results, fields): """Get data grouped by users. Includes extra user info.""" ats = AgileToolsSystem(self.env) sp = SimplifiedPermissions(self.env) tickets_json = defaultdict(lambda: defaultdict(dict)) all_users = [] user_data = {} use_avatar = self.config.get('avatar','mode').lower() != 'off' # TODO: allow the task board to respect user groups for group, data in sp.group_memberships().items(): for member in data['members']: if member.sid not in user_data: all_users.append(member.sid); user_data[member.sid] = { 'name': member.get("name", member.sid), 'avatar': use_avatar and req.href.avatar(member.sid) or None } def name_for_sid(sid): return user_data[sid]["name"] if sid in user_data else sid options = [""] + sorted(all_users, key=name_for_sid) for result in results: ticket = Ticket(self.env, result['id']) filtered_result = dict((k, v) for k, v in result.iteritems() if k in fields) filtered_result['position'] = ats.position(result['id']) filtered_result['_changetime'] = to_utimestamp(result['changetime']) # we use Trac's to_json() (through add_script_data), so # we'll replace any types which can't be json serialised for k, v in filtered_result.items(): if isinstance(v, datetime): filtered_result[k] = pretty_age(v) group_field_val = ticket.get_value_or_default(field["name"]) or "" tickets_json[group_field_val][result["id"]] = filtered_result return (field["name"], tickets_json, options, user_data)
def _get_standard_data_(self, req, milestone, field, results, fields): """Get ticket information when no custom grouped-by method present.""" ats = AgileToolsSystem(self.env) tickets_json = defaultdict(lambda: defaultdict(dict)) # Allow for the unset option options = [""] + [option for option in field["options"]] for result in results: ticket = Ticket(self.env, result['id']) filtered_result = dict((k, v) for k, v in result.iteritems() if k in fields) filtered_result['position'] = ats.position(result['id']) filtered_result['_changetime'] = to_utimestamp(result['changetime']) # we use Trac's to_json() (through add_script_data), so # we'll replace any types which can't be json serialised for k, v in filtered_result.items(): if isinstance(v, datetime): filtered_result[k] = pretty_age(v) group_field_val = ticket.get_value_or_default(field["name"]) or "" tickets_json[group_field_val][result["id"]] = filtered_result return (field["name"], tickets_json, options)
def get_timeline_markup(self, req, call, maxrows=10): """ Generates the markup needed when this component is called both explicitly inside wiki pages and implicitly by the ISideBarBoxProvider. Note this code uses methods from the trac TimelineModule module. """ chrome = Chrome(self.env) # last 14 days should be enough stop = datetime.now(req.tz) start = stop - timedelta(days=50) # use code from trac/timeline to generate event data timeline = TimelineModule(self.env) available_filters, filters = timeline.get_filters(req) include_authors, exclude_authors = timeline.authors() events = timeline.get_events(req, start, stop, filters, available_filters, include_authors, exclude_authors, maxrows) show_gravatar = self.config.get('avatar','mode').lower() != 'off' # create the mark up context = Context.from_request(req) event_list = tag.ul(class_="timeline-list no-style") for event in events: event_title = event['render']('title', context) event_url = event['render']('url', context) event_list.append(tag.li( show_gravatar and tag.img( src=req.href.avatar(event['author'] if event['author'] else 'anonymous'), class_="avatar", ) or "", tag.span( chrome.authorinfo(req, event['author']), class_="author" ), tag.span( pretty_age(event['date']), class_="date", ), tag.div( tag.i(class_="event-type fa fa-" + event['kind']), tag.a( event_title, href=event_url, ), class_="event-summary" ), class_="cf" )) # if the markup is being generated via ISideBarBoxProvider we don't # need to add a span3 class div_classes = "box-sidebar" if call == "macro": div_classes += " span3 right" return tag.div( tag.h3( tag.i( class_="fa fa-calendar" ), " Recent Project Activity" ), event_list, class_=div_classes, )
def _get_status_data(self, req, milestone, field, results, fields): """Get data grouped by WORKFLOW and status. It's not possible to show tickets in different workflows on the same taskboard, so we create an additional outer group for workflows. We then get the workflow with the most tickets, and show that first""" ats = AgileToolsSystem(self.env) loc = LogicaOrderController(self.env) # Data for status much more complex as we need to track the workflow tickets_json = defaultdict(lambda: defaultdict(dict)) by_type = defaultdict(int) by_wf = defaultdict(int) wf_for_type = {} # Store the options required in order to complete an action # E.g. closing a ticket requires a resolution act_controls = {} for r in results: # Increment type statistics by_type[r['type']] += 1 tkt = Ticket(self.env, r['id']) if r['type'] not in wf_for_type: wf_for_type[r['type']] = \ loc._get_workflow_for_typename(r['type']) wf = wf_for_type[r['type']] state = loc._determine_workflow_state(tkt, req=req) op = Operation(self.env, wf, state) filtered = dict((k, v) for k, v in r.iteritems() if k in fields) filtered['position'] = ats.position(r['id']) filtered['_changetime'] = to_utimestamp(r['changetime']) # we use Trac's to_json() (through add_script_data), so # we'll replace any types which can't be json serialised for k, v in filtered.items(): if isinstance(v, datetime): filtered[k] = pretty_age(v) filtered['actions'] = self._get_status_actions(req, op, wf, state) # Collect all actions requiring further input self._update_controls(req, act_controls, filtered['actions'], tkt) tickets_json[wf.name][r["status"]][r["id"]] = filtered # Calculate number of tickets per workflow for ty in by_type: by_wf[wf_for_type[ty]] += by_type[ty] wf_statuses = dict((wf.name, wf.ordered_statuses) for wf in by_wf) # Retrieve Kanban-style status limits db = self.env.get_read_db() cursor = db.cursor() cursor.execute(""" SELECT status, hardlimit FROM kanban_limits WHERE milestone = %s""", (milestone,)) status_limits = dict((limit[0], limit[1]) for limit in cursor) # Initially show the most used workflow show_first = max(by_wf, key=lambda n: by_wf[n]).name return ("status", tickets_json, wf_statuses, status_limits, show_first, act_controls)