def walk_tagged_names(self, names, tags, predicate): db = self.env.get_db_cnx() cursor = db.cursor() tags = set(tags) names = set(names) args = [] sql = "SELECT * FROM (SELECT id, %s, %s AS allfields FROM ticket) s" % ( ','.join(self.fields), '||'.join( ["COALESCE(%s, '')" % f for f in self.fields])) constraints = [] if names: constraints.append("id IN (" + ', '.join(['%s' for n in names]) + ")") args += [unicode(n) for n in names] if tags: constraints.append("(" + ' OR '.join(["allfields LIKE %s" for t in tags]) + ")") args += ['%' + t + '%' for t in tags] if constraints: sql += " WHERE " + " AND ".join(constraints) sql += " ORDER BY id" cursor.execute(sql, args) for row in cursor: id, ttags = row[0], ' '.join([f for f in row[1:-1] if f]) ticket_tags = set(self._keyword_split.findall(ttags)) if not tags or ticket_tags.intersection(tags): if predicate(id, ticket_tags): yield (id, ticket_tags)
def walk_tagged_names(self, names, tags, predicate): db = self.env.get_db_cnx() cursor = db.cursor() tags = set(tags) names = set(names) args = [] sql = "SELECT * FROM (SELECT id, %s, %s AS allfields FROM ticket) s" % (','.join(self.fields), '||'.join(["COALESCE(%s, '')" % f for f in self.fields])) constraints = [] if names: constraints.append("id IN (" + ', '.join(['%s' for n in names]) + ")") args += [unicode(n) for n in names] if tags: constraints.append("(" + ' OR '.join(["allfields LIKE %s" for t in tags]) + ")") args += ['%' + t + '%' for t in tags] if constraints: sql += " WHERE " + " AND ".join(constraints) sql += " ORDER BY id" cursor.execute(sql, args) for row in cursor: id, ttags = row[0], ' '.join([f for f in row[1:-1] if f]) ticket_tags = set(self._keyword_split.findall(ttags)) if not tags or ticket_tags.intersection(tags): if predicate(id, ticket_tags): yield (id, ticket_tags)
def get_name_tags(self, name): db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute( "SELECT keywords FROM ticket WHERE id=%s AND keywords IS NOT NULL AND keywords != ''", (name, )) for row in cursor: return set(self._keyword_split.findall(row[0]))
def walk_tagged_names(self, names, tags, predicate): db = self.env.get_db_cnx() cursor = db.cursor() tags = set(tags) names = set(names) args = [] sql = "SELECT id, keywords FROM ticket WHERE keywords IS NOT NULL AND keywords != ''" if names: sql += " AND id IN (" + ', '.join(['%s' for n in names]) + ")" args += [unicode(n) for n in names] if tags: sql += " AND (" + ' OR '.join(["keywords LIKE %s" for t in tags]) + ")" args += ['%' + t + '%' for t in tags] sql += " ORDER BY id" cursor.execute(sql, args) for id, ttags in cursor: ticket_tags = set(self._keyword_split.findall(ttags)) if not tags or ticket_tags.intersection(tags): if predicate(id, ticket_tags): yield (id, ticket_tags)
def render_tagcloud(self, req, smallest=10, biggest=20, showcount=True, tagspace=None, mincount=1, tagspaces=[], **kwargs): """ This macro displays a [http://en.wikipedia.org/wiki/Tag_cloud tag cloud] (weighted list) of all tags. ||'''Argument'''||'''Description'''|| ||`tagspace=<tagspace>`||Specify the tagspace the macro should operate on.|| ||`tagspaces=(<tagspace>,...)`||Specify a set of tagspaces the macro should operate on.|| ||`smallest=<n>`||The lower bound of the font size for the tag cloud.|| ||`biggest=<n>`||The upper bound of the font size for the tag cloud.|| ||`showcount=true|false`||Show the count of objects for each tag?|| ||`mincount=<n>`||Hide tags with a count less than `<n>`.|| """ smallest = int(smallest) biggest = int(biggest) mincount = int(mincount) engine = TagEngine(self.env) # Get wiki tagspace if tagspace: tagspaces = [tagspace] else: tagspaces = tagspaces or engine.tagspaces cloud = {} for tag, names in engine.get_tags(tagspaces=tagspaces, detailed=True).iteritems(): count = len(names) if count >= mincount: cloud[tag] = len(names) tags = cloud.keys() # No tags? if not tags: return '' # by_count maps tag counts to an index in the set of counts by_count = list(set(cloud.values())) by_count.sort() by_count = dict([(c, float(i)) for i, c in enumerate(by_count)]) taginfo = self._tag_details({}, tags) tags.sort() rlen = float(biggest - smallest) tlen = float(len(by_count)) scale = 1.0 if tlen: scale = rlen / tlen out = StringIO() out.write('<ul class="tagcloud">\n') last = tags[-1] for tag in tags: if tag == last: cls = ' class="last"' else: cls = '' if showcount != 'false': count = ' <span class="tagcount">(%i)</span>' % cloud[tag] else: count = '' out.write( '<li%s><a rel="tag" title="%s" style="font-size: %ipx" href="%s">%s</a>%s</li>\n' % (cls, taginfo[tag][1] + ' (%i)' % cloud[tag], smallest + int(by_count[cloud[tag]] * scale), taginfo[tag][0], tag, count)) out.write('</ul>\n') return out.getvalue()
def render_listtagged(self, req, *tags, **kwargs): """ List tagged objects. Optionally accepts a list of tags to match against. The special tag '''. (dot)''' inserts the current Wiki page name. `[[ListTagged(<tag>, ...)]]` ||'''Argument'''||'''Description'''|| ||`tagspace=<tagspace>`||Specify the tagspace the macro should operate on.|| ||`tagspaces=(<tagspace>,...)`||Specify a set of tagspaces the macro should operate on.|| ||`operation=intersection|union`||The set operation to perform on the discovered objects.|| ||`showheadings=true|false`||List objects under the tagspace they occur in.|| ||`expression=<expr>`||Match object tags against the given expression.|| The supported expression operators are: unary - (not); binary +, - and | (and, and not, or). All values in the expression are treated as tags. Any tag not in the same form as a Python variable must be quoted. eg. Match all objects tagged with ticket and workflow, and not tagged with wiki or closed. (ticket+workflow)-(wiki|closed) If an expression is provided operation is ignored. """ if 'tagspace' in kwargs: tagspaces = [kwargs.get('tagspace', None)] else: tagspaces = kwargs.get('tagspaces', '') or \ list(TagEngine(self.env).tagspaces) expression = kwargs.get('expression', None) showheadings = kwargs.get('showheadings', 'false') operation = kwargs.get('operation', 'intersection') if operation not in ('union', 'intersection'): raise TracError("Invalid tag set operation '%s'" % operation) engine = TagEngine(self.env) page_name = self._current_page(req) if page_name: tags = [tag == '.' and page_name or tag for tag in tags] tags = set(tags) taginfo = {} out = StringIO() out.write('<ul class="listtagged">\n') # If expression was passed as an argument, do a full walk, using the # expression as the predicate. Silently assumes that failed expressions # are normal tags. if expression: from tractags.expr import Expression try: expr = Expression(expression) self.env.log.debug(repr(expr)) except Exception, e: self.env.log.error("Invalid expression '%s'" % expression, exc_info=True) tags.update([ x.strip() for x in re.split('[+,]', expression) if x.strip() ]) expression = None else: self.env.log.debug(expr.ast) tagged_names = {} tags.update(expr.get_tags()) for tagspace, name, name_tags in engine.walk_tagged_names( tags=tags, tagspaces=tagspaces, predicate=lambda ts, n, t: expr(t)): tagged_names.setdefault(tagspace, {})[name] = name_tags tagged_names = [ (tagspace, names) for tagspace, names in tagged_names.iteritems() ]
def get_name_tags(self, name): db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT keywords FROM ticket WHERE id=%s AND keywords IS NOT NULL AND keywords != ''", (name,)) for row in cursor: return set(self._keyword_split.findall(row[0]))
def _ticket_tags(self, ticket): return set(self._keyword_split.findall(' '.join([ticket[f] for f in self.fields])))
def render_tagcloud(self, req, smallest=10, biggest=20, showcount=True, tagspace=None, mincount=1, tagspaces=[]): """ This macro displays a [http://en.wikipedia.org/wiki/Tag_cloud tag cloud] (weighted list) of all tags. ||'''Argument'''||'''Description'''|| ||`tagspace=<tagspace>`||Specify the tagspace the macro should operate on.|| ||`tagspaces=(<tagspace>,...)`||Specify a set of tagspaces the macro should operate on.|| ||`smallest=<n>`||The lower bound of the font size for the tag cloud.|| ||`biggest=<n>`||The upper bound of the font size for the tag cloud.|| ||`showcount=true|false`||Show the count of objects for each tag?|| ||`mincount=<n>`||Hide tags with a count less than `<n>`.|| """ smallest = int(smallest) biggest = int(biggest) mincount = int(mincount) engine = TagEngine(self.env) # Get wiki tagspace if tagspace: tagspaces = [tagspace] else: tagspaces = tagspaces or engine.tagspaces cloud = {} for tag, names in engine.get_tags(tagspaces=tagspaces, detailed=True).iteritems(): count = len(names) if count >= mincount: cloud[tag] = len(names) tags = cloud.keys() # No tags? if not tags: return '' # by_count maps tag counts to an index in the set of counts by_count = list(set(cloud.values())) by_count.sort() by_count = dict([(c, float(i)) for i, c in enumerate(by_count)]) taginfo = self._tag_details({}, tags) tags.sort() rlen = float(biggest - smallest) tlen = float(len(by_count)) scale = 1.0 if tlen: scale = rlen / tlen out = StringIO() out.write('<ul class="tagcloud">\n') last = tags[-1] for tag in tags: if tag == last: cls = ' class="last"' else: cls = '' if showcount != 'false': count = ' <span class="tagcount">(%i)</span>' % cloud[tag] else: count = '' out.write('<li%s><a rel="tag" title="%s" style="font-size: %ipx" href="%s">%s</a>%s</li>\n' % ( cls, taginfo[tag][1] + ' (%i)' % cloud[tag], smallest + int(by_count[cloud[tag]] * scale), taginfo[tag][0], tag, count)) out.write('</ul>\n') return out.getvalue()
def render_listtagged(self, req, *tags, **kwargs): """ List tagged objects. Optionally accepts a list of tags to match against. The special tag '''. (dot)''' inserts the current Wiki page name. `[[ListTagged(<tag>, ...)]]` ||'''Argument'''||'''Description'''|| ||`tagspace=<tagspace>`||Specify the tagspace the macro should operate on.|| ||`tagspaces=(<tagspace>,...)`||Specify a set of tagspaces the macro should operate on.|| ||`operation=intersection|union`||The set operation to perform on the discovered objects.|| ||`showheadings=true|false`||List objects under the tagspace they occur in.|| ||`expression=<expr>`||Match object tags against the given expression.|| The supported expression operators are: unary - (not); binary +, - and | (and, and not, or). All values in the expression are treated as tags. Any tag not in the same form as a Python variable must be quoted. eg. Match all objects tagged with ticket and workflow, and not tagged with wiki or closed. (ticket+workflow)-(wiki|closed) If an expression is provided operation is ignored. """ if 'tagspace' in kwargs: tagspaces = [kwargs.get('tagspace', None)] else: tagspaces = kwargs.get('tagspaces', '') or \ list(TagEngine(self.env).tagspaces) expression = kwargs.get('expression', None) showheadings = kwargs.get('showheadings', 'false') operation = kwargs.get('operation', 'intersection') if operation not in ('union', 'intersection'): raise TracError("Invalid tag set operation '%s'" % operation) engine = TagEngine(self.env) page_name = req.hdf.get('wiki.page_name') if page_name: tags = [tag == '.' and page_name or tag for tag in tags] tags = set(tags) taginfo = {} out = StringIO() out.write('<ul class="listtagged">\n') # If expression was passed as an argument, do a full walk, using the # expression as the predicate. Silently assumes that failed expressions # are normal tags. if expression: from tractags.expr import Expression try: expr = Expression(expression) except Exception, e: self.env.log.error("Invalid expression '%s'" % expression, exc_info=True) tags.update([x.strip() for x in re.split('[+,]', expression) if x.strip()]) expression = None else: self.env.log.debug(expr.ast) tagged_names = {} tags.update(expr.get_tags()) for tagspace, name, name_tags in engine.walk_tagged_names(tags=tags, tagspaces=tagspaces, predicate=lambda ts, n, t: expr(t)): tagged_names.setdefault(tagspace, {})[name] = name_tags tagged_names = [(tagspace, names) for tagspace, names in tagged_names.iteritems()]
def _ticket_tags(self, ticket): return set(self._keyword_split.findall(ticket['keywords']))
def _ticket_tags(self, ticket): return set( self._keyword_split.findall(' '.join( [ticket[f] for f in self.fields])))