--- /dev/null
+<html><head></head><body>Hello world</body></html>
--- /dev/null
+Content-type: text/html
+
+
+Date: Mon, 22 Jun 2009 20:05:00 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+
+In-reply-to: c454aa67-ca30-43e8-9be4-58cbddd01b63
+
--- /dev/null
+Excerpt from my mail to the list on Sat, 20 Jun 2009 21:55:54 -0400:
+
+On Mon, Nov 24, 2008 at 07:15:08PM -0500, Aaron Bentley wrote:
+> 576:om: Allow attachments
+> Sensible.
+
+I'm not as convinced they are a good idea as I once was. I've just
+added comments-from-stdin, e.g.
+ some-invalid-command | be comment <bug-id> -
+Which is mostly what I'd be using attachments for anyway. If you
+really want to support the attachments/mime-types etc. like we had
+maybe been leaning towards before, you'd need to look at the output of
+`be show ...' with an email client, which seems a bit excessive. Do
+we even want mime types at all? With the xml output a la Thomas, you
+should be able to pipe into whatever sort of `viewer' you want, and it
+doesn't end up being hardcoded into the main repo.
+
+
+Notes since my email:
+
+be->xml->mutt has since been implemented, and it preserves comment
+mime-type. This allows those that want to go crazy to attach whatever
+they want to their comments:
+
+ $ echo "<html><head></head><body>Hello world</body></html>" | be comment --content-type text/html 576:2 -
+
+I think non-text attachments without a browser/mail-viewer don't make
+sense, so I'm closing this bug. Feel free to keep it open in your own
+repo, or argue with me on the list ;).
+
--- /dev/null
+Content-type: text/plain
+
+
+Date: Mon, 22 Jun 2009 20:03:27 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+
+In-reply-to: d83a5436-85e3-42c7-9a89-a6d50df9d279
+
severity: minor
-status: open
+status: closed
summary: Allow attachments
--- /dev/null
+From Aaron's Mon, 24 Nov 2008 19:15:09 -0500 email:
+
+8e9:om: list X most recent entries
+Closeable. (And yes, I would do it instead of 'be diff')
--- /dev/null
+Content-type: text/plain
+
+
+Date: Mon, 22 Jun 2009 19:46:45 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+Content-type: text/plain
-
-Content-type=text/plain
-
-
-
-
-
-
-Date=Mon, 24 Nov 2008 13:10:38 +0000
-
-
-
-
-
-
-From=W. Trevor King <wking@drexel.edu>
+Date: Mon, 24 Nov 2008 13:10:38 +0000
+From: W. Trevor King <wking@drexel.edu>
+creator: abentley
+severity: minor
-creator=abentley
+status: closed
+summary: list X most recent entries
-
-severity=minor
-
-
-
-
-
-
-status=open
-
-
-
-
-
-
-summary=list X most recent entries
-
-
-
-
-
-
-time=Wed, 25 Jan 2006 15:44:18 +0000
-
-
+time: Wed, 25 Jan 2006 15:44:18 +0000
--- /dev/null
+Presumably this would be to allow sorting of bugs by last-modified
+date instead of by creation date. With the xml output, this is no
+longer needed. For example, I view bugs in mutt with
+ $ be list | xml/be-xml-to-mbox | xml/catmutt
+and use mutt to sort the threads by last-modified, e.g. by adding
+ set sort=threads
+ set sort_aux=last-date
+to my ~/.muttrc.
+
+That being said, I could go for a user-specified sort command in
+becommands/list.py, rather than the current bug.cmp_full, since other
+mail readers may suck more than mutt ;), and even mutt might not have
+that perfect sort you desire coded into it :p. The problem is that
+while the cmp_* functions in bug are short, they are not really the
+sort of thing you'd want to type in on the command line. Perhaps we
+can just slowly accumulate a rich array of bug.cmp_* functions as
+they are requested, and allow the user to prepend their favorites to
+the default cmp_full list...
+
--- /dev/null
+Content-type: text/plain
+
+
+Date: Mon, 22 Jun 2009 18:40:43 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
--- /dev/null
+User specfied sort added, along with bug.cmp_last_modified.
+
+Hmm, perhaps you don't want the last comment date, but e.g. the last
+time one of the bug attributes are changed. In that case, I suggest
+ bzr log .be/bugs/9ce2f015-8ea0-43a5-a03d-fc36f6d202fe/
+
+Maybe log(file) functionality should be incorperated into libbe/rcs...
+Perhaps accessed through a --history. I'm not sure I remember enough
+Arch to do that ;).
--- /dev/null
+Content-type: text/plain
+
+
+Date: Mon, 22 Jun 2009 19:43:21 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+creator: abentley
+severity: minor
-creator=abentley
+status: open
+summary: Add last-modified field to bugs
-
-severity=minor
-
-
-
-
-
-
-status=open
-
-
-
-
-
-
-summary=Add last-modified field to bugs
-
-
-
-
-
-
-time=Thu, 14 Sep 2006 18:08:53 +0000
-
-
+time: Thu, 14 Sep 2006 18:08:53 +0000
--- /dev/null
+In my Tue, 25 Nov 2008 08:30:19 -0500 email:
+
+I thought feature requests would just have "wishlist" severity. What
+would be an example of a to-do item that is not a feature request or a
+bug?
+
--- /dev/null
+Content-type: text/plain
+
+
+Date: Mon, 22 Jun 2009 20:12:35 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+
+In-reply-to: 354dcfc6-5997-4ffe-b7a0-baa852213539
+
--- /dev/null
+In Aaron's Mon, 24 Nov 2008 19:15:08 -0500 email, he adds:
+
+Issue trackers should provide tracking of
+1. bugs
+2. feature requests
+3. to-do items.
+
--- /dev/null
+Content-type: text/plain
+
+
+Date: Mon, 22 Jun 2009 20:11:02 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
--- /dev/null
+If you want more granularity than just `wishlist' what about the
+`severities':
+ todo-critical
+ todo-minor
+ todo-...
+Then get a list of available severities with
+ $ be list --help | grep -A1 '^severity'
+ severity
+ wishlist,minor,serious,critical,fatal,todo-critical,todo-minor
+And show all the todos:
+ $ be list --severity todo-critical,todo-minor
+
+You can configure all the severities you'd like with
+ $ be set severity wishlist,minor,...
--- /dev/null
+Content-type: text/plain
+
+
+Date: Mon, 22 Jun 2009 20:20:39 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+
+In-reply-to: f847c981-873e-41ae-b5ce-83dfe60b9afe
+
--- /dev/null
+In Aaron's Tue, 25 Nov 2008 09:32:29 -0500 email:
+
+I think that approach doesn't give features the richness they need.
+Features also have severities-- some features are important, and others
+are just nice-to-have. And there should be a way to list *only* bugs,
+or *only* features.
+
+In a bug tracker, "wishlist" is either an aberration, or it means a very
+low severity.
+
--- /dev/null
+Content-type: text/plain
+
+
+Date: Mon, 22 Jun 2009 20:14:26 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+
+In-reply-to: 22348320-40d3-422c-bdf0-0f6a6bde3fab
+
+creator: abentley
+severity: minor
-creator=abentley
+status: closed
+summary: Support 'issues', like todo, better
-
-severity=minor
-
-
-
-
-
-
-status=open
-
-
-
-
-
-
-summary=Support 'issues', like todo, better
-
-
-
-
-
-
-time=Wed, 04 Jan 2006 21:09:02 +0000
-
-
+time: Wed, 04 Jan 2006 21:09:02 +0000
--- /dev/null
+From Aaron's Mon, 24 Nov 2008 19:15:09 -0500 email
+
+cf5:oc: OK, maybe not fatal, but how about a new name that suggests
+process tracking, not just bugs?
+
+If you can come with a better name, that would be great. But naming an
+issue tracker for its bug-tracking features isn't a terrible idea.
+
--- /dev/null
+Content-type: text/plain
+
+
+Date: Mon, 22 Jun 2009 19:48:44 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
body+='\n'
comment = parent.new_reply(body=body)
+ if options.content_type != None:
+ comment.content_type = options.content_type
bd.save()
def get_parser():
parser = cmdutil.CmdOptionParser("be comment ID [COMMENT]")
+ parser.add_option("-c", "--content-type", metavar="MIME", dest="content_type",
+ help="Set comment content-type (e.g. text/plain)", default=None)
return parser
longhelp="""
import os
__desc__ = __doc__
+# get a list of * for cmp_*() comparing two bugs.
+AVAILABLE_CMPS = [fn[4:] for fn in dir(bug) if fn[:4] == 'cmp_']
+AVAILABLE_CMPS.remove("attr") # a cmp_* template.
+
def execute(args, test=False):
"""
>>> import os
complete(options, args, parser)
if len(args) > 0:
raise cmdutil.UsageError("Too many arguments.")
+ cmp_list = []
+ if options.sort_by != None:
+ for cmp in options.sort_by.split(','):
+ if cmp not in AVAILABLE_CMPS:
+ raise cmdutil.UserError("Invalid sort on '%s'.\nValid sorts:\n %s"
+ % (cmp, '\n '.join(AVAILABLE_CMPS)))
+ cmp_list.append(eval('bug.cmp_%s' % cmp))
bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test)
bd.load_all_bugs()
print "No matching bugs found"
def list_bugs(cur_bugs, title=None, just_uuids=False, xml=False):
- cur_bugs.sort(bug.cmp_full)
if xml == True:
print "<bugs>"
if len(cur_bugs) > 0:
print bg.string(shortlist=True)
if xml == True:
print "</bugs>"
-
+
+ # sort bugs
+ cmp_list.extend(bug.DEFAULT_CMP_FULL_CMP_LIST)
+ cmp_fn = bug.BugCompoundComparator(cmp_list=cmp_list)
+ bugs.sort(cmp_fn)
+
+ # print list of bugs
list_bugs(bugs, just_uuids=options.uuids, xml=options.xml)
def get_parser():
help="List options matching ASSIGNED", default=None)
parser.add_option("-t", "--target", metavar="TARGET", dest="target",
help="List options matching TARGET", default=None)
+ parser.add_option("-S", "--sort", metavar="SORT-BY", dest="sort_by",
+ help="Adjust bug-sort criteria with comma-separated list SORT-BY. e.g. \"--sort creator,time\". Available criteria: %s" % ','.join(AVAILABLE_CMPS), default=None)
# boolean options. All but uuids and xml are special cases of long forms
bools = (("u", "uuids", "Only print the bug UUIDS"),
("w", "wishlist", "List bugs with 'wishlist' severity"),
msg += '\n '.join(bd.settings_properties)
raise cmdutil.UserError(msg)
old_setting = bd.settings.get(args[0])
- try:
- setattr(bd, args[0], args[1])
- except bugdir.InvalidValue, e:
- bd.settings[args[0]] = old_setting
- bd.save()
- raise cmdutil.UserError(e)
+ setattr(bd, args[0], args[1])
else:
del bd.settings[args[0]]
bd.save()
else:
shortname = self.bugdir.bug_shortname(self)
if shortlist == False:
- if self.time_string == "":
- timestring = self.time_string
+ if self.time == None:
+ timestring = ""
else:
htime = utility.handy_time(self.time)
timestring = "%s (%s)" % (htime, self.time_string)
# chronological rankings (newer < older)
cmp_time = lambda bug_1, bug_2 : cmp_attr(bug_1, bug_2, "time", invert=True)
-def cmp_full(bug_1, bug_2, cmp_list=(cmp_status,cmp_severity,cmp_assigned,
- cmp_time,cmp_creator)):
- for comparison in cmp_list :
- val = comparison(bug_1, bug_2)
- if val != 0 :
- return val
- return 0
-
-class InvalidValue(ValueError):
- def __init__(self, name, value):
- msg = "Cannot assign value %s to %s" % (value, name)
- Exception.__init__(self, msg)
- self.name = name
- self.value = value
+DEFAULT_CMP_FULL_CMP_LIST = \
+ (cmp_status,cmp_severity,cmp_assigned,cmp_time,cmp_creator)
+
+class BugCompoundComparator (object):
+ def __init__(self, cmp_list=DEFAULT_CMP_FULL_CMP_LIST):
+ self.cmp_list = cmp_list
+ def __call__(self, bug_1, bug_2):
+ for comparison in self.cmp_list :
+ val = comparison(bug_1, bug_2)
+ if val != 0 :
+ return val
+ return 0
+
+cmp_full = BugCompoundComparator()
+
+
+# define some bonus cmp_* functions
+def cmp_last_modified(bug_1, bug_2):
+ """
+ Like cmp_time(), but use most recent comment instead of bug
+ creation for the timestamp.
+ """
+ def last_modified(bug):
+ time = bug.time
+ for comment in bug.comment_root.traverse():
+ if comment.time > time:
+ time = comment.time
+ return time
+ val_1 = last_modified(bug_1)
+ val_2 = last_modified(bug_2)
+ return -cmp(val_1, val_2)
+
suite = doctest.DocTestSuite()
Exception.__init__(self,
"Specified root is already initialized: %s" % path)
-class InvalidValue(ValueError):
- def __init__(self, name, value):
- msg = "Cannot assign value %s to %s" % (value, name)
- Exception.__init__(self, msg)
- self.name = name
- self.value = value
-
class MultipleBugMatches(ValueError):
def __init__(self, shortname, matches):
msg = ("More than one bug matches %s. "