def _prepare_links(self, tkt, db): links = TicketLinks(self.env, tkt, db) links.blocking = set( int(n) for n in self.NUMBERS_RE.findall(tkt['blocking'] or '')) links.blocked_by = set( int(n) for n in self.NUMBERS_RE.findall(tkt['blockedby'] or '')) return links
def _get_stats_config(self): all_statuses = set(TicketSystem(self.env).get_all_status()) remaining_statuses = set(all_statuses) groups = DefaultTicketGroupStatsProvider(self.env)._get_ticket_groups() catch_all_group = None for group in groups: status_str = group['status'].strip() if status_str == '*': if catch_all_group: raise TracError(_( "'%(group1)s' and '%(group2)s' milestone groups " "both are declared to be \"catch-all\" groups. " "Please check your configuration.", group1=group['name'], group2=catch_all_group['name'])) catch_all_group = group else: group_statuses = set([s.strip() for s in status_str.split(',')]) \ & all_statuses if group_statuses - remaining_statuses: raise TracError(_( "'%(groupname)s' milestone group reused status " "'%(status)s' already taken by other groups. " "Please check your configuration.", groupname=group['name'], status=', '.join(group_statuses - remaining_statuses))) else: remaining_statuses -= group_statuses group['statuses'] = group_statuses if catch_all_group: catch_all_group['statuses'] = remaining_statuses return groups
def sort(self): """Do an in-place topological sort of this prototype.""" from api import TracForgeAdminSystem steps = TracForgeAdminSystem(self.env).get_project_setup_participants() all_provides = set() for action, args in self: all_provides |= set(steps[action].get('provides', ())) effective_depends = {} for action, args in self: # All real deps are always used effective_depends.setdefault(action, []).extend(steps[action].get('depends', ())) for tag in steps[action].get('optional_depends', ()): # Any optional dep that is provided by something else is used if tag in all_provides: effective_depends[action].append(tag) old = set([action for action, args in self]) new = [] tags = set() for i in xrange(len(self)): for action in old: self.env.log.debug('TracForge: %s %s %s %s %s', i, action, old, new, tags) if all([tag in tags for tag in effective_depends[action]]): new.append(action) tags |= set(steps[action].get('provides', [])) old.remove(action) break if not old: break if old: raise ValueError('Cant solve') action_map = dict(self) self[:] = [(action, action_map[action]) for action in new]
def __init__(self, env, tkt, db=None, ticket_cache=None): '''Initialize ticket links Use `ticket_cache` (if is not None) to store fetched tickets. ''' self.env = env if not isinstance(tkt, Ticket): if ticket_cache is not None: tid = int(tkt) if tid not in ticket_cache: ticket_cache[tid] = Ticket(self.env, tid) tkt = ticket_cache[tid] else: tkt = Ticket(self.env, tkt) self.tkt = tkt db = db or self.env.get_db_cnx() cursor = db.cursor() cursor.execute('SELECT dest FROM mastertickets WHERE source=%s ORDER BY dest', (self.tkt.id,)) self.blocking = set([int(num) for num, in cursor]) self._old_blocking = copy.copy(self.blocking) cursor.execute('SELECT source FROM mastertickets WHERE dest=%s ORDER BY source', (self.tkt.id,)) self.blocked_by = set([int(num) for num, in cursor]) self._old_blocked_by = copy.copy(self.blocked_by)
def _update_pages(self): all_pages = WikiSystem(self.env).get_pages() self.pages = set([p for p in all_pages if len(p) >= self.minimum_length]) exclude = set([p.strip() for p in (self.exclude or '') if p.strip()]) self.pages.difference_update(exclude) explicitly_wikified = set([p.strip() for p in (self.explicitly_wikify or '') if p.strip()]) self.pages.update(explicitly_wikified)
def get_user_permissions(self, username): """Retrieve the permissions for the given user and return them in a dictionary. The permissions are stored in the database as (username, action) records. There's simple support for groups by using lowercase names for the action column: such a record represents a group and not an actual permission, and declares that the user is part of that group. """ subjects = set([username]) for provider in self.group_providers: subjects.update(provider.get_permission_groups(username)) actions = set([]) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT username,action FROM permission") rows = cursor.fetchall() while True: num_users = len(subjects) num_actions = len(actions) for user, action in rows: if user in subjects: if action.isupper() and action not in actions: actions.add(action) if not action.isupper() and action not in subjects: # action is actually the name of the permission group # here subjects.add(action) if num_users == len(subjects) and num_actions == len(actions): break return list(actions)
def post_process_request(self, req, template, data, content_type): if req.path_info.startswith('/ticket/'): # In case of an invalid ticket, the data is invalid if not data: return template, data, content_type tkt = data['ticket'] links = TicketLinks(self.env, tkt) for i in links.blocked_by: if Ticket(self.env, i)['status'] != 'closed': add_script(req, 'mastertickets/disable_resolve.js') break # Add link to depgraph if needed if links: add_ctxtnav(req, 'Depgraph', req.href.depgraph('ticket', tkt.id)) for change in data.get('changes', {}): if not change.has_key('fields'): continue for field, field_data in change['fields'].iteritems(): if field in self.fields: if field_data['new'].strip(): new = set( [int(n) for n in field_data['new'].split(',')]) else: new = set() if field_data['old'].strip(): old = set( [int(n) for n in field_data['old'].split(',')]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(add)]))) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(sub)]))) elms.append(u' removed') field_data['rendered'] = elms #add a link to generate a dependency graph for all the tickets in the milestone if req.path_info.startswith('/milestone/'): if not data: return template, data, content_type milestone = data['milestone'] add_ctxtnav(req, 'Depgraph', req.href.depgraph('milestone', milestone.name)) return template, data, content_type
def ticket_deleted(self, tkt): db = self.env.get_db_cnx() links = TicketLinks(self.env, tkt, db) links.blocking = set() links.blocked_by = set() links.save('trac', 'Ticket #%s deleted' % tkt.id, when=None, db=db) db.commit()
def ticket_changed(self, tkt, comment, author, old_values): db = self.env.get_db_cnx() links = TicketLinks(self.env, tkt, db) links.blocking = set(self.NUMBERS_RE.findall(tkt['blocking'] or '')) links.blocked_by = set(self.NUMBERS_RE.findall(tkt['blockedby'] or '')) links.save(author, comment, tkt.time_changed, db) db.commit()
def testSprintTicketStatsChartUsesAliases(self): self.env.compmgr.enabled[SprintTicketStatsChartGenerator] = True self.teh.create_ticket(Type.USER_STORY, {Key.SPRINT: self.sprint.name}) get_widget = ChartGenerator(self.env).get_chartwidget widget = get_widget(ChartType.SPRINT_TICKET_STATS, sprint_name=self.sprint.name) chart_labels = set([item[1] for item in widget.data["labels"]]) self.assert_equals(set(["User Story", "Task"]), chart_labels)
def _update_pages(self): all_pages = WikiSystem(self.env).get_pages() self.pages = set( [p for p in all_pages if len(p) >= self.minimum_length]) exclude = set([p.strip() for p in (self.exclude or '') if p.strip()]) self.pages.difference_update(exclude) explicitly_wikified = set( [p.strip() for p in (self.explicitly_wikify or '') if p.strip()]) self.pages.update(explicitly_wikified)
def ticket_deleted(self, tkt): db = self.env.get_db_cnx() links = TicketLinks(self.env, tkt, db) links.blocking = set() links.blocked_by = set() links.save('trac', 'Ticket #%s deleted'%tkt.id, when=None, db=db) db.commit()
def post_process_request(self, req, template, data, content_type): if req.path_info.startswith('/ticket/'): # In case of an invalid ticket, the data is invalid if not data: return template, data, content_type tkt = data['ticket'] links = TicketLinks(self.env, tkt) for i in links.blocked_by: if Ticket(self.env, i)['status'] != 'closed': add_script(req, 'mastertickets/disable_resolve.js') break # Add link to depgraph if needed if links: add_ctxtnav(req, 'Depgraph', req.href.depgraph(tkt.id)) for change in data.get('changes', {}): if not change.has_key('fields'): continue for field, field_data in change['fields'].iteritems(): if field in self.fields: if field_data['new'].strip(): new = set([int(n) for n in field_data['new'].split(',')]) else: new = set() if field_data['old'].strip(): old = set([int(n) for n in field_data['old'].split(',')]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(add)])) ) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(sub)])) ) elms.append(u' removed') field_data['rendered'] = elms #add a link to generate a dependency graph for all the tickets in the milestone if req.path_info.startswith('/milestone/'): if not data: return template, data, content_type milestone=data['milestone'] add_ctxtnav(req, 'Depgraph', req.href.depgraph('milestone', milestone.name)) return template, data, content_type
def testSprintTicketStatsChartUsesAliases(self): self.env.compmgr.enabled[SprintTicketStatsChartGenerator] = True self.teh.create_ticket(Type.USER_STORY, {Key.SPRINT: self.sprint.name}) get_widget = ChartGenerator(self.env).get_chartwidget widget = get_widget(ChartType.SPRINT_TICKET_STATS, sprint_name=self.sprint.name) chart_labels = set([item[1] for item in widget.data['labels']]) self.assert_equals(set(['User Story', 'Task']), chart_labels)
def _field_names_for_backlog_types(self): field_names = set() ticket_config = AgiloConfig(self.env).ticket_configuration for type_name in self.ticket_types: # don't trust the type_name in self.ticket_types, backlog admin page # does not do validation on that if type_name not in ticket_config.fieldnames_per_type: continue fields_for_this_type = ticket_config.fieldnames_per_type[type_name] field_names.update(set(fields_for_this_type)) return field_names
def post_process_request(self, req, template, data, content_type, method=None): if req.path_info.startswith('/ticket/'): # In case of an invalid ticket, the data is invalid if not data: return template, data, content_type, method tkt = data['ticket'] with self.env.db_query as db: links = CrashDumpTicketLinks(self.env, tkt, db=db) for change in data.get('changes', {}): if not change.has_key('fields'): continue for field, field_data in change['fields'].iteritems(): if field in self.crashdump_link_fields: if field_data['new'].strip(): new = set([ CrashDumpSystem.get_crash_id(n) for n in field_data['new'].split(',') ]) else: new = set() if field_data['old'].strip(): old = set([ CrashDumpSystem.get_crash_id(n) for n in field_data['old'].split(',') ]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(add)]))) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(sub)]))) elms.append(u' removed') field_data['rendered'] = elms links.crashes = new return template, data, content_type, method
def post_process_request(self, req, template, data, content_type): if req.path_info.startswith('/ticket/'): tkt = data['ticket'] links = TicketLinks(self.env, tkt) for i in links.blocked_by: if Ticket(self.env, i)['status'] != 'closed': add_script(req, 'mastertickets/disable_resolve.js') break data['mastertickets'] = { 'field_values': { 'blocking': linkify_ids(self.env, req, links.blocking), 'blockedby': linkify_ids(self.env, req, links.blocked_by), }, } # Add link to depgraph if needed if links: add_ctxtnav(req, 'Depgraph', req.href.depgraph(tkt.id)) for change in data.get('changes', []): for field, field_data in change['fields'].iteritems(): if field in self.fields: if field_data['new'].strip(): new = set( [int(n) for n in field_data['new'].split(',')]) else: new = set() if field_data['old'].strip(): old = set( [int(n) for n in field_data['old'].split(',')]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(add)]))) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(sub)]))) elms.append(u' removed') field_data['rendered'] = elms return template, data, content_type
def post_process_request(self, req, template, origData, content_type): if req.path_info.startswith('/newticket'): mode = 'new' elif req.path_info.startswith('/ticket/'): mode = 'view' else: return template, origData, content_type fieldData = {} fieldData['condfields'] = {} all_fields = [] standard_fields = set() for f in TicketSystem(self.env).get_ticket_fields(): all_fields.append(f['name']) if not f.get('custom'): standard_fields.add(f['name']) if 'owner' in all_fields: curr_idx = all_fields.index('owner') if 'cc' in all_fields: insert_idx = all_fields.index('cc') else: insert_idx = len(all_fields) if curr_idx < insert_idx: all_fields.insert(insert_idx, all_fields[curr_idx]) del all_fields[curr_idx] for t in self.types: fieldData['condfields'][t] = self.get_fields(t, all_fields, standard_fields) # fields = set(getattr(self, t+'_fields')) # if self.include_std: # fields.update(standard_fields) # fields.update(self.forced_fields) # fieldData['condfields'][t] = dict([ # (f, f in fields) for f in all_fields # ]) self.log.debug(all_fields) self.log.info(standard_fields) fieldData['mode'] = mode fieldData['all_fields'] = list(all_fields) fieldData['ok_view_fields'] = sorted(set(all_fields) - self.forced_fields, key=lambda x: all_fields.index(x)) fieldData['ok_new_fields'] = sorted((set(all_fields) - self.forced_fields) - set(['owner']), key=lambda x: all_fields.index(x)) add_script_data(req, fieldData) add_script(req, '/condfields.js') return template, origData, content_type
def _get_metric_groups(self, available_metrics): # Some metrics are well known and we show them together in one chart. # We have the RT_USP ratio separate because of two reasons: # - it has not much to do with the other charts # - the numbers are much lower so we would need to add a second scale # which is not yet implemented in the team metrics chart. chart_groups = [(Key.ESTIMATED_VELOCITY, Key.VELOCITY), (Key.CAPACITY, Key.COMMITMENT), (Key.RT_USP_RATIO, )] grouped_metrics = [] [grouped_metrics.extend(i) for i in chart_groups] other_metrics = set(available_metrics).difference(set(grouped_metrics)) other_metrics = [(i,) for i in other_metrics] return chart_groups + other_metrics
def post_process_request(self, req, template, data, content_type): if req.path_info.startswith('/ticket/'): tkt = data['ticket'] links = TicketLinks(self.env, tkt) for i in links.blocked_by: if Ticket(self.env, i)['status'] != 'closed': add_script(req, 'mastertickets/disable_resolve.js') break data['mastertickets'] = { 'field_values': { 'blocking': linkify_ids(self.env, req, links.blocking), 'blockedby': linkify_ids(self.env, req, links.blocked_by), }, } # Add link to depgraph if needed if links: add_ctxtnav(req, 'Depgraph', req.href.depgraph(tkt.id)) for change in data.get('changes', []): for field, field_data in change['fields'].iteritems(): if field in self.fields: if field_data['new'].strip(): new = set([int(n) for n in field_data['new'].split(',')]) else: new = set() if field_data['old'].strip(): old = set([int(n) for n in field_data['old'].split(',')]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(add)])) ) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(sub)])) ) elms.append(u' removed') field_data['rendered'] = elms return template, data, content_type
def process_request(self, req): data = {} ticket_types = {} field_types = {} mode = req.path_info[12:-3] if mode != 'new' and mode != 'view': raise TracError('Invalid condfields view') all_fields = [] standard_fields = set() for f in TicketSystem(self.env).get_ticket_fields(): all_fields.append(f['name']) field_types[f['name']] = f['type'] if not f.get('custom'): standard_fields.add(f['name']) if 'owner' in all_fields: curr_idx = all_fields.index('owner') if 'cc' in all_fields: insert_idx = all_fields.index('cc') else: insert_idx = len(all_fields) if curr_idx < insert_idx: all_fields.insert(insert_idx, all_fields[curr_idx]) del all_fields[curr_idx] for t in self.types: if not self.show_default: hiddenfields = set(getattr(self, t+'_fields')) fields = set(all_fields) fields.difference_update(hiddenfields) else: fields = set(getattr(self, t+'_fields')) if self.include_std: fields.update(standard_fields) fields.update(set(self.forced_fields)) ticket_types[t] = dict([ (f, f in fields) for f in all_fields ]) self.log.debug(all_fields) self.log.info(standard_fields) data['mode'] = mode data['types'] = json.dumps(ticket_types) data['field_types'] = json.dumps(field_types) data['required_fields'] = json.dumps(list(self.forced_fields)) return 'condfields.js', {'condfields': data}, 'text/plain'
def ticket_changed(self, tkt, comment, author, old_values): db = self.env.get_db_cnx() links = TicketLinks(self.env, tkt, db) old_relations = {'blocking': set([]), 'blockedby': set([])} if "blocking" in old_values: old_relations['blocking'] = extract_ticket_ids(old_values['blocking']) if "blockedby" in old_values: old_relations['blockedby'] = extract_ticket_ids(old_values['blockedby']) links.save(old_relations, author, comment, tkt.time_changed, db) db.commit()
def get_tagged_resources(self, req, tags): if not self.check_permission(req.perm, "view"): return db = self.env.get_db_cnx() cursor = db.cursor() args = [self.realm] sql = "SELECT DISTINCT name FROM tags WHERE tagspace=%s" if tags: sql += " AND tags.tag IN (%s)" % ", ".join(["%s" for t in tags]) args += tags sql += " ORDER by name" cursor.execute(sql, args) resources = {} for (name,) in cursor: resource = Resource(self.realm, name) if self.check_permission(req.perm(resource), "view"): resources[resource.id] = resource if not resources: return args = [self.realm] + list(resources) # XXX Is this going to be excruciatingly slow? sql = "SELECT DISTINCT name, tag FROM tags WHERE tagspace=%%s AND " "name IN (%s) ORDER BY name" % ", ".join( ["%s" for _ in resources] ) cursor.execute(sql, args) for name, tags in groupby(cursor, lambda row: row[0]): resource = resources[name] yield resource, set([tag[1] for tag in tags])
def query(self, req, query="", attribute_handlers=None): """Return a sequence of (resource, tags) tuples matching a query. Query syntax is described in tractags.query. :param attribute_handlers: Register additional query attribute handlers. See Query documentation for more information. """ def realm_handler(_, node, context): return query.match(node, [context.realm]) all_attribute_handlers = {"realm": realm_handler} all_attribute_handlers.update(attribute_handlers or {}) if re.search(r"(expression|tagspace|tagspaces|operation|showheadings" "|expression)=", query): message = Markup( "You seem to be using an old Tag query. " 'Try using the <a href="%s">new syntax</a> in your ' "<strong>ListTagged</strong> macro.", req.href("tags"), ) add_warning(req, message) query = Query(query, attribute_handlers=all_attribute_handlers) query_tags = set(query.terms()) for provider in self.tag_providers: for resource, tags in provider.get_tagged_resources(req, query_tags): if query(tags, context=resource): yield resource, tags
def render_announcement_preference_box(self, req, panel): supported_realms = {} for producer in self.producers: for realm in producer.realms(): for distributor in self.distributors: for transport in distributor.transports(): for fmtr in self.formatters: for style in fmtr.styles(transport, realm): if realm not in supported_realms: supported_realms[realm] = set() supported_realms[realm].add(style) if req.method == "POST": for realm in supported_realms: opt = req.args.get('email_format_%s' % realm, False) if opt: req.session['announcer_email_format_%s' % realm] = opt prefs = {} for realm in supported_realms: prefs[realm] = req.session.get('announcer_email_format_%s' % realm, None) or self._get_default_format() data = dict( realms=supported_realms, preferences=prefs, ) return "prefs_announcer_email.html", data
def __str__(self): edges = [] nodes = [] memo = set() def process(lst): for obj in lst: if obj in memo: continue memo.add(obj) if isinstance(obj, Node): nodes.append(obj) process(obj.edges) elif isinstance(obj, Edge): edges.append(obj) if isinstance(obj.source, Node): process((obj.source, )) if isinstance(obj.dest, Node): process((obj.dest, )) process(self.nodes) process(self.edges) lines = [u'digraph "%s" {' % self.name] for att, value in self.attributes.iteritems(): lines.append(u'\t%s="%s";' % (att, value)) for obj in itertools.chain(nodes, edges): lines.append(u'\t%s;' % obj) lines.append(u'}') return u'\n'.join(lines)
def get_all_status(self): """Returns a sorted list of all the states all of the action controllers know about.""" valid_states = set() for controller in self.action_controllers: valid_states.update(controller.get_all_status()) return sorted(valid_states)
def set_tags(self, req, resource, tags): """Set tags on a resource. Existing tags are replaced. """ return self._get_provider(resource.realm) \ .set_resource_tags(req, resource, set(tags))
def __init__(self, env, tkt, db=None): self.env = env if not isinstance(tkt, Ticket): tkt = Ticket(self.env, tkt) self.tkt = tkt db = db or self.env.get_db_cnx() cursor = db.cursor() cursor.execute('SELECT dest FROM mastertickets WHERE source=%s ORDER BY dest', (self.tkt.id,)) self.blocking = set([int(num) for num, in cursor]) self._old_blocking = copy.copy(self.blocking) cursor.execute('SELECT source FROM mastertickets WHERE dest=%s ORDER BY source', (self.tkt.id,)) self.blocked_by = set([int(num) for num, in cursor]) self._old_blocked_by = copy.copy(self.blocked_by)
def validate_test_case(self, req, postname, version, fields): if 'testcase-preview' in req.args: return [] qa_res = Resource('qa', postname, version) if req.perm(qa_res).has_permission('QA_ADMIN'): return [] if version > 1: bp = BlogPost(self.env, postname, version) last_post_fields = bp._fetch_fields(version=version - 1) else: last_post_fields = {} field_names = set(fields).union(last_post_fields) changes = [] for field in field_names: old = to_unicode(last_post_fields.get(field, '')) new = to_unicode(fields.get(field, '')) if new and old != new: changes.append((old, new)) author = fields.get('author', '') if arity(FilterSystem.test) == 4: # 0.11 compatible method signature FilterSystem(self.env).test(req, author, changes) else: # 0.12+ compatible that adds an 'ip' argument FilterSystem(self.env).test(req, author, changes, req.remote_addr) return []
def render_announcement_preference_box(self, req, panel): supported_realms = {} for producer in self.producers: for realm in producer.realms(): for distributor in self.distributors: for transport in distributor.transports(): for fmtr in self.formatters: for style in fmtr.styles(transport, realm): if realm not in supported_realms: supported_realms[realm] = set() supported_realms[realm].add(style) settings = {} for realm in supported_realms: name = 'xmpp_format_%s' % realm settings[realm] = SubscriptionSetting( self.env, name, XmppDistributor(self.env).xmpp_format_setting.default) if req.method == "POST": for realm, setting in settings.items(): name = 'xmpp_format_%s' % realm setting.set_user_setting(req.session, req.args.get(name), save=False) req.session.save() prefs = {} for realm, setting in settings.items(): prefs[realm] = setting.get_user_setting(req.session.sid)[0] data = dict( realms=supported_realms, preferences=prefs, ) return "prefs_announcer_xmpp.html", data
def validate_blog_post(self, req, postname, version, fields): if 'blog-preview' in req.args: return [] blog_res = Resource('blog', postname, version) if req.perm(blog_res).has_permission('BLOG_ADMIN'): return [] if version > 1: bp = BlogPost(self.env, postname, version) last_post_fields = bp._fetch_fields(version=version-1) else: last_post_fields = {} field_names = set(fields).union(last_post_fields) changes = [] for field in field_names: old = to_unicode(last_post_fields.get(field, '')) new = to_unicode(fields.get(field, '')) if new and old != new: changes.append((old, new)) author = fields.get('author', '') if arity(FilterSystem.test) == 4: # 0.11 compatible method signature FilterSystem(self.env).test(req, author, changes) else: # 0.12+ compatible that adds an 'ip' argument FilterSystem(self.env).test(req, author, changes, req.remote_addr) return []
def _all_configured_ticket_fields(self): field_names = set() ticket_config = AgiloConfig(self.env).ticket_configuration for field_names_for_type in ticket_config.fieldnames_per_type.values(): for field_name in field_names_for_type: field_names.add(field_name) return list(field_names)
def render_cloud(env, req, cloud, renderer=None): """Render a tag cloud :cloud: Dictionary of {object: count} representing the cloud. :param renderer: A callable with signature (tag, count, percent) used to render the cloud objects. """ min_px = 10.0 max_px = 30.0 scale = 1.0 if renderer is None: def default_renderer(tag, count, percent): href = get_resource_url(env, Resource('tag', tag), req.href) return builder.a(tag, rel='tag', title='%i' % count, href=href, style='font-size: %ipx' % int(min_px + percent * (max_px - min_px))) renderer = default_renderer # A LUT from count to n/len(cloud) size_lut = dict([(c, float(i)) for i, c in enumerate(sorted(set([r for r in cloud.values()])))]) if size_lut: scale = 1.0 / len(size_lut) ul = builder.ul(class_='tagcloud') last = len(cloud) - 1 for i, (tag, count) in enumerate(sorted(cloud.iteritems())): percent = size_lut[count] * scale li = builder.li(renderer(tag, count, percent)) if i == last: li(class_='last') li() ul(li) return ul
def ticket_deleted(self, tkt): with self.env.db_transaction as db: links = CrashDumpTicketLinks(self.env, tkt, db) links.crashes = set() links.save('trac', 'Ticket #%s deleted'%tkt.id, when=None, db=db) db.commit()
def get_tagged_resources(self, req, tags): if 'TAGS_VIEW' not in req.perm or 'QA_VIEW' not in req.perm: return db = self.env.get_db_cnx() cursor = db.cursor() args = [] constraints = [] sql = "SELECT bp1.name, bp1.categories, bp1.version " \ "FROM qa_testcases bp1," \ "(SELECT name, max(version) AS ver " \ "FROM qa_testcases GROUP BY name) bp2 " \ "WHERE bp1.version = bp2.ver AND bp1.name = bp2.name" if tags: constraints.append( "(" + ' OR '.join(["bp1.categories LIKE %s" for t in tags]) + ")") args += ['%' + t + '%' for t in tags] else: constraints.append("bp1.categories != ''") if constraints: sql += " AND " + " AND ".join(constraints) sql += " ORDER BY bp1.name" self.env.log.debug(sql) cursor.execute(sql, args) for row in cursor: post_name, categories = row[0], set(_parse_categories(row[1])) if not tags or categories.intersection(tags): resource = Resource('blog', post_name) if 'QA_VIEW' in req.perm(resource) and \ 'TAGS_VIEW' in req.perm(resource): yield (resource, categories)
def _fetch_fields(self, version=0): """ Returns a dict with field/value combinations for the content of a specific version of a blog post, or last/current version if version is 0. Returns emtpy dict if no such post or post/version exists. """ self.versions = self.get_versions() if not self.versions or (version and not version in self.versions): # No blog post with the name exists return {} version = version or self.versions[-1] cnx = self.env.get_db_cnx() cursor = cnx.cursor() cursor.execute("SELECT title, body, publish_time, version_time, " "version_comment, version_author, author, categories " "FROM fullblog_posts " "WHERE name=%s AND version=%s", (self.name, version) ) fields = {} for row in cursor: fields['version'] = version fields['title'] = row[0] fields['body'] = row[1] fields['publish_time'] = to_datetime(row[2], utc) fields['version_time'] = to_datetime(row[3], utc) fields['version_comment'] = row[4] fields['version_author'] = row[5] fields['author'] = row[6] fields['categories'] = row[7] fields['category_list'] = set(_parse_categories(row[7])) return fields
def query(self, req, query='', attribute_handlers=None): """Return a sequence of (resource, tags) tuples matching a query. Query syntax is described in tractags.query. :param attribute_handlers: Register additional query attribute handlers. See Query documentation for more information. """ def realm_handler(_, node, context): return query.match(node, [context.realm]) all_attribute_handlers = { 'realm': realm_handler, } all_attribute_handlers.update(attribute_handlers or {}) if re.search( r'(expression|tagspace|tagspaces|operation|showheadings' '|expression)=', query): message = Markup( 'You seem to be using an old Tag query. ' 'Try using the <a href="%s">new syntax</a> in your ' '<strong>ListTagged</strong> macro.', req.href('tags')) add_warning(req, message) query = Query(query, attribute_handlers=all_attribute_handlers) query_tags = set(query.terms()) for provider in self.tag_providers: for resource, tags in provider.get_tagged_resources( req, query_tags): if query(tags, context=resource): yield resource, tags
def _get_available_metrics(self, metrics_by_sprint): "Return a list containing the keys of all available metrics" available_metrics = set() for sprint, metrics in metrics_by_sprint: for metrics_name in metrics: available_metrics.add(metrics_name) return list(available_metrics)
def get_tagged_resources(self, req, tags): if not self.check_permission(req.perm, 'view'): return db = self.env.get_db_cnx() cursor = db.cursor() args = [self.realm] sql = 'SELECT DISTINCT name FROM tags WHERE tagspace=%s' if tags: sql += ' AND tags.tag IN (%s)' % ', '.join(['%s' for t in tags]) args += tags sql += ' ORDER by name' cursor.execute(sql, args) resources = {} for name, in cursor: resource = Resource(self.realm, name) if self.check_permission(req.perm(resource), 'view'): resources[resource.id] = resource if not resources: return args = [self.realm] + list(resources) # XXX Is this going to be excruciatingly slow? sql = 'SELECT DISTINCT name, tag FROM tags WHERE tagspace=%%s AND ' \ 'name IN (%s) ORDER BY name' % ', '.join(['%s' for _ in resources]) cursor.execute(sql, args) for name, tags in groupby(cursor, lambda row: row[0]): resource = resources[name] yield resource, set([tag[1] for tag in tags])
def _get_groups(self, user): # Get initial subjects groups = set([user]) for provider in self.group_providers: for group in provider.get_permission_groups(user): groups.add(group) # Essentially the default trac PermissionStore ignores user provided # groups so we have to look them up manually: # changed this to only do this for the default permission # store this has been reported as broken/very slow for the # LDAP permission store ps = PermissionSystem(self.env) if isinstance(ps.store, DefaultPermissionStore): perms = ps.get_all_permissions() repeat = True while repeat: repeat = False for subject, action in perms: if subject in groups and not action.isupper() and action not in groups: groups.add(action) repeat = True return groups
def __str__(self): edges = [] nodes = [] memo = set() def process(lst): for obj in lst: if obj in memo: continue memo.add(obj) if isinstance(obj, Node): nodes.append(obj) process(obj.edges) elif isinstance(obj, Edge): edges.append(obj) if isinstance(obj.source, Node): process((obj.source,)) if isinstance(obj.dest, Node): process((obj.dest,)) process(self.nodes) process(self.edges) lines = [u'digraph "%s" {' % self.name] for att, value in self.attributes.iteritems(): lines.append(u'\t%s="%s";' % (att, value)) for obj in itertools.chain(nodes, edges): lines.append(u'\t%s;' % obj) lines.append(u'}') return u'\n'.join(lines)
def _fetch_fields(self, version=0): """ Returns a dict with field/value combinations for the content of a specific version of a blog post, or last/current version if version is 0. Returns emtpy dict if no such post or post/version exists. """ self.versions = self.get_versions() if not self.versions or (version and not version in self.versions): # No blog post with the name exists return {} version = version or self.versions[-1] sql = "SELECT title, body, publish_time, version_time, " \ "version_comment, version_author, author, categories " \ "FROM fullblog_posts " \ "WHERE name=%s AND version=%s" args = (self.name, version) if hasattr(self.env, 'db_query'): cursor = self.env.db_query(sql, args) else: db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute(sql, args) fields = {} for row in cursor: fields['version'] = version fields['title'] = row[0] fields['body'] = row[1] fields['publish_time'] = to_datetime(row[2], utc) fields['version_time'] = to_datetime(row[3], utc) fields['version_comment'] = row[4] fields['version_author'] = row[5] fields['author'] = row[6] fields['categories'] = row[7] fields['category_list'] = set(_parse_categories(row[7])) return fields
def get_tagged_resources(self, req, tags): if 'TAGS_VIEW' not in req.perm or 'BLOG_VIEW' not in req.perm: return db = self.env.get_db_cnx() cursor = db.cursor() args = [] constraints = [] sql = "SELECT bp1.name, bp1.categories, bp1.version " \ "FROM fullblog_posts bp1," \ "(SELECT name, max(version) AS ver " \ "FROM fullblog_posts GROUP BY name) bp2 " \ "WHERE bp1.version = bp2.ver AND bp1.name = bp2.name" if tags: constraints.append("(" + ' OR '.join( ["bp1.categories LIKE %s" for t in tags]) + ")") args += ['%' + t + '%' for t in tags] else: constraints.append("bp1.categories != ''") if constraints: sql += " AND " + " AND ".join(constraints) sql += " ORDER BY bp1.name" self.env.log.debug(sql) cursor.execute(sql, args) for row in cursor: post_name, categories = row[0], set(_parse_categories(row[1])) if not tags or categories.intersection(tags): resource = Resource('blog', post_name) if 'BLOG_VIEW' in req.perm(resource) and \ 'TAGS_VIEW' in req.perm(resource): yield (resource, categories)
def get_months_authors_categories(self, from_dt=None, to_dt=None, user=None, perm=None): """ Returns a structure of post metadata: ([ ((year1, month1), count), ((year1, month2), count) ], # newest first [ (author1, count), (author2, count) ], # alphabetical [ (category1, count), (category2, count) ], # alphabetical total) # num of posts * Use 'from_dt' and 'to_dt' (datetime objects) to restrict search to posts with a publish_time within the intervals (None means ignore). * If user and perm is provided, the list is also filtered for permissions. * Note also that it only fetches from most recent version. """ blog_posts = get_blog_posts(self.env, from_dt=from_dt, to_dt=to_dt) a_dict = {} c_dict = {} m_dict = {} total = 0 for post in blog_posts: if user and perm: # Check permissions bp = BlogPost(self.env, post[0], post[1]) if not 'BLOG_VIEW' in perm(bp.resource): continue # Skip this post post_time = post[2] m_dict[(post_time.year, post_time.month)] = m_dict.get( (post_time.year, post_time.month), 0) + 1 author = post[3] a_dict[author] = a_dict.get(author, 0) + 1 categories = post[6] # a list for category in set(categories): c_dict[category] = c_dict.get(category, 0) + 1 total += 1 return ([(m, m_dict.get(m, 0)) for m in sorted(m_dict.keys(), reverse=True)], [(a, a_dict.get(a, 0)) for a in sorted(a_dict.keys())], [(c, c_dict.get(c, 0)) for c in sorted(c_dict.keys())], total)
def tickets_for_crash(db, crashid): cursor = db.cursor() #print('tickets_for_crash db=%s, crashid=%s %s' % (db, crashid, type(crashid))) cursor.execute('SELECT ticket FROM crashdump_ticket WHERE crash=%s ORDER BY ticket', (crashid,)) return set([int(num) for num, in cursor])
def attr_modifier(name, event): attrs = event[1][1] class_list = attrs.get(name, '').split() self.log.debug('BH Theme : Element classes ' + str(class_list)) out_classes = ' '.join(set(class_list + classes)) self.log.debug('BH Theme : Inserting class ' + out_classes) return out_classes
def test_can_extract_contingent_from_readonly_page(self): expected_contingent = dict(name='foo', amount='6.0', used='2.0') page = TeamSprintDetailTester(None, 'Foo Team', 'Foo Sprint', html=no_delete_fixture) self.assertEqual(set(['foo']), page.contingent_names()) self.assertEqual(expected_contingent, page.contingent_for_name('foo'))
def _real_send(self, evt): """Accepts a single AnnouncementEvent instance (or subclass), and returns nothing. There is no way (intentionally) to determine what the AnnouncementSystem did with a particular event besides looking through the debug logs. """ try: supported_subscribers = [] for sp in self.subscribers: categories = sp.get_subscription_categories(evt.realm) if categories: if ('*' in categories) or (evt.category in categories): supported_subscribers.append(sp) self.log.debug( "AnnouncementSystem found the following subscribers capable of" " handling '%s, %s': %s" % (evt.realm, evt.category, ', '.join([ss.__class__.__name__ for ss in \ supported_subscribers])) ) subscriptions = set() for sp in supported_subscribers: subscriptions.update( x for x in sp.get_subscriptions_for_event(evt) if x ) self.log.debug( "AnnouncementSystem has found the following subscriptions: " \ "%s"%(', '.join(['[%s(%s) via %s]' % ((s[1] or s[3]),\ s[2] and 'authenticated' or 'not authenticated',s[0])\ for s in subscriptions] ) ) ) packages = {} for transport, sid, authenticated, address in subscriptions: if transport not in packages: packages[transport] = set() packages[transport].add((sid,authenticated,address)) for distributor in self.distributors: transport = distributor.get_distribution_transport() if transport in packages: distributor.distribute(transport, packages[transport], evt) except: self.log.error("AnnouncementSystem failed.", exc_info=True)