Exemplo n.º 1
0
def resolve(ui, db, prefix, resolution=None, *args, **opts):
    '''Marks an issue resolved
    
    If the issue is not simply "resolved", for instance
    it is concluded it will not be fixed, or it lacks information,
    it may be considered resolved nevertheless.  Therefore you can
    specify a custom resolved status.
    '''
    try:
        if resolution and db.meta_prefix['resolution']:
            resolution = db.meta_prefix['resolution'][resolution]
    except error.UnknownPrefix as err:
        raise error.Abort("%s is not a valid option for resolution" %
                          err.prefix)
    except error.AmbiguousPrefix as err:
        raise error.Abort(
            "%s is an ambiguous option for resolution, choices: %s" %
            (err.prefix, util.list2str(err.choices)))

    iss = db.get_issue(prefix)
    if iss.resolution:
        raise error.Abort(
            "Cannot resolve issue %s, it is already resolved with resolution %s."
            "Use open to reopen a resolved issue." %
            (db.iss_prefix.pref_str(iss.id, True), iss.resolution))

    iss.status = ui.config('metadata', 'status.resolved')
    iss.resolution = resolution or ui.config('metadata', 'resolution.default')

    iss.to_JSON(db.issues)

    ui.write("Resolved issue %s with resolution %s" %
             (db.iss_prefix.pref_str(iss.id, True), iss.resolution))
Exemplo n.º 2
0
def update(ui, db, prefix, *args, **opts):
    '''Updates the information associated with an issue'''

    # metadata
    try:
        metas = ['issue', 'severity', 'status', 'resolution', 'category']
        for meta in metas:
            try:
                if opts[meta]:
                    if db.meta_prefix[meta]:
                        opts[meta] = db.meta_prefix[meta][opts[meta]]
            except Exception as err:
                err.cause = meta
                raise err
    except error.UnknownPrefix as err:
        raise error.Abort("%s is not a valid option for %s" %
                          (err.prefix, err.cause))
    except error.AmbiguousPrefix as err:
        raise error.Abort("%s is an ambiguous option for %s, choices: %s" %
                          (err.prefix, err.cause, util.list2str(err.choices)))

    iss = db.get_issue(prefix)
    origiss = db.get_issue(prefix)
    if len(opts) == 0:
        raise error.Abort("Did not specify any updates to make to issue %s" %
                          db.iss_prefix.pref_str(iss.id, True))

    if opts['assign_to']:
        iss.assigned_to = db.get_user(opts['assign_to'])
    if opts['listener']:
        iss.listeners.extend([db.get_user(i) for i in opts['listener']])
    if opts['rl']:
        for l in [db.get_user(i) for i in opts['rl']]:
            iss.listeners.remove(l)
    if opts['issue']:
        iss.issue = opts['issue']
    if opts['target']:
        iss.target = opts['target']
    if opts['severity']:
        iss.severity = opts['severity']
    if opts['status']:
        iss.status = opts['status']
    if opts['category']:
        iss.category = opts['category']

    iss.to_JSON(db.issues)

    ui.write("Updated issue %s" % db.iss_prefix.pref_str(iss.id, True))
    ui.write(iss.descChanges(origiss, ui))
Exemplo n.º 3
0
    def get_user(self, prefix):
        try:
            ret = self.usr_prefix[prefix]
            if ret == 'me' or ret == 'nobody':
                return None
            return ret

        except error.AmbiguousPrefix as err:
            raise error.Abort(
                "User prefix %s is ambiguous\n  Suggestions: %s" %
                (err.prefix, err.choices))
        except error.UnknownPrefix as err:
            raise error.Abort(
                "User prefix %s does not correspond to any known user" %
                err.prefix)
Exemplo n.º 4
0
def child(ui, db, child_pref, parent_pref, *args, **opts):
    '''Mark an issue as a child of another issue
    
    This simply affects the relationship between the
    issues, and does not, for instance, prevent a user
    from resolving the parent issue if they so desire.
    '''
    child = db.get_issue(child_pref)
    parent = db.get_issue(parent_pref)

    if child.parent:
        if not ui.confirm(
                "Issue %s is already a child of issue %s, do you really want to change it's parent to issue %s?"
                %
            (child_pref, db.iss_prefix.prefix(child.parent), parent_pref),
                True):
            raise error.Abort("Did not change issue %s's parent." % child_pref)
        orig = db.get_issue(child.parent)
        orig.children.remove(child.id)
        orig.to_JSON(db.issues)

    child.parent = parent.id
    parent.children.append(child.id)

    child.to_JSON(db.issues)
    parent.to_JSON(db.issues)

    ui.write("Marked issue %s as a child of issue %s" %
             (child_pref, parent_pref))
    return 0
Exemplo n.º 5
0
def comment(ui, db, pref, *args, **opts):
    '''Add a comment to an issue
    
    Appends a time and potentially user-stamped comment
    to the specified issue.  If -m,--message is passed,
    the specified message is used as the comment text.
    Otherwise, an editor is launched and the user is prompted
    to construct a comment.
    '''

    iss = db.get_issue(pref)

    if opts['message']:
        message = opts['message'].strip()
    else:
        lines = util.ab_strip(
            ui.edit("AB: Commenting on Issue %s:  %s\n"
                    "AB: Lines starting with 'AB:' are ignored.\n\n" %
                    (db.iss_prefix.prefix(iss.id), iss.title)))
        message = ''.join(lines).strip()

    if message == '':
        raise error.Abort("Must provide a comment for the specified issue.")

    comment = [ui.config('ui', 'username'), time.time(), message]
    iss.comments.append(comment)

    iss.to_JSON(db.issues)

    ui.write("Added Comment to Issue %s:" % db.iss_prefix.prefix(iss.id))
    ui.write(issue.comment_to_str(comment, ui))

    return 0
Exemplo n.º 6
0
def list(ui, db, *args, **opts):
    '''Get a list of open issues
    
    Called without arguments, list will display all open issues.
    The -r,--resolved flag will instead display resolved issues.
    
    Use the other parameters, detailed below, to further filter
    the issues you wish to see.
    '''

    if opts['assigned_to'] != '*':
        user = db.get_user(
            opts['assigned_to']) if opts['assigned_to'] else None
    if opts['listener']:
        lstset = set([db.get_user(i) for i in opts['listener']])

    # Metadata
    metas = ['issue', 'severity', 'status', 'category', 'resolution']
    for meta in metas:
        try:
            if opts[meta] and db.meta_prefix[meta]:
                opts[meta] = db.meta_prefix[meta][opts[meta]]
        except error.AmbiguousPrefix as err:
            raise error.Abort("%s is an ambiguous option for %s, choices: %s" %
                              (err.prefix, meta, util.list2str(err.choices)))
        except Exception as err:
            pass  # do nothing, it's not a valid prefix

    iss_iter = (
        i for i in db.get_issues()
        if (bool(i.resolution) == bool(opts['resolved'])) and (
            opts['assigned_to'] == '*' or i.assigned_to == user) and
        (not opts['listener'] or not set(i.listeners).isdisjoint(lstset)) and (
            not opts['issue'] or i.issue == opts['issue']) and
        (not opts['target'] or i.target == opts['target']) and (
            not opts['severity'] or i.severity == opts['severity']) and (
                not opts['status'] or i.status == opts['status']) and (
                    not opts['category'] or i.category == opts['category']) and
        (not opts['resolution'] or i.resolution == opts['resolution']) and (
            not opts['creator'] or i.creator == db.get_user(opts['creator']))
        and (not opts['grep'] or opts['grep'].lower() in i.title.lower()))

    count = 0

    # for now, if the user wants to slow down output, they must pipe output through less/more
    # we ought to be able to do this for them in certain cases
    for i in iss_iter:
        ui.quiet(db.iss_prefix.prefix(i.id), ln=False)
        ui.write(":\t%s" % i.title, ln=False)
        ui.quiet()
        count += 1

    ui.write("Found %s matching issue%s" %
             (count if count > 0 else "no", "" if count == 1 else "s"))

    return 0 if count > 0 else 1
Exemplo n.º 7
0
    def get_issue_id(self, pref):
        try:
            return self.iss_prefix[pref]
        except error.AmbiguousPrefix as err:

            def choices(issLs):
                ls = (self.get_issue(i)
                      for i in (issLs[:2] if len(issLs) > 3 else issLs[:]))
                return ', '.join((self.iss_prefix.prefix(i.id) +
                                  (':' + i.title if i.title else '')
                                  for i in ls))

            raise error.Abort(
                "Issue prefix %s is ambiguous\n  Suggestions: %s" %
                (err.prefix, choices(err.choices)))
        except error.UnknownPrefix as err:
            raise error.Abort(
                "Issue prefix %s does not correspond to any issues" %
                err.prefix)
Exemplo n.º 8
0
def open_iss(ui, db, prefix, status=None, *args, **opts):
    '''Opens a previously resolved issue
    
    This command reopens the issue, and optionally sets its
    status to the passed status.'''
    iss = db.get_issue(prefix)
    if not iss.resolution:
        raise error.Abort("Cannot open issue %s, it is already open.\n"
                          "Use resolve to close an open issue." %
                          db.iss_prefix.pref_str(iss.id, True))

    iss.status = status or ui.config('metadata', 'status.opened')
    iss.resolution = None

    iss.to_JSON(db.issues)

    ui.write("Reopened issue %s, set status to %s" %
             (db.iss_prefix.pref_str(iss.id, True), iss.status))
Exemplo n.º 9
0
def init(ui, dir='.', *args, **opts):
    '''Initialize an Abundant database
    
    Creates an '.ab' directory in the specified directory,
    or the cwd if not otherwise set.
    '''
    db = database.DB(dir, False)
    if db.exists():
        raise error.Abort("Abundant database already exists.")
    # don't need to make db.db because makedirs handles that
    os.makedirs(db.issues)
    os.mkdir(db.cache)
    with open(db.conf, 'w'):  # as conf:
        # write any initial configuration to config file
        pass
    with open(db.local_conf, 'w'):  # as lconf:
        # write any initial configuration to local config file
        pass
    with open(db.users, 'w'):  # as usr
        pass

    ui.write("Created Abundant issue database in %s" % db.path)
Exemplo n.º 10
0
def exec(cmds, cwd):
    exec_timer = util.Timer("Full command execution")
    try:
        ui_load_timer = util.Timer("UI load")
        ui = usrint.UI()
        ui_load_timer.stop(
        )  # since we haven't parsed --debug yet, we can't use ui.debug()
    except:
        sys.stderr.write("FAILED TO CREATE UI OBJECT.\n"
                         "This should not have been possible.\n"
                         "Please report this issue immediately.\n\n")
        raise
    try:
        parse_timer = util.Timer("Command parsing")
        if len(cmds) < 1 or (len(cmds[0]) > 0 and cmds[0][0] == '-'):
            prefix = commands.fallback_cmd
            args = cmds
        else:
            prefix = cmds[0]
            args = cmds[1:]

        # load config settings

        try:
            task = cmdPfx[prefix]

        except error.UnknownPrefix as err:
            ui.alert("Unknown Command: %s" % err.prefix)
            task = commands.fallback_cmd
            args = []
        except error.AmbiguousPrefix as err:
            ui.alert("Ambiguous Command: %s" % err.prefix)
            ui.alert("Did you mean: %s" % util.list2str(err.choices))
            task = commands.fallback_cmd
            args = []

        func, options, args_left = _parse(task, args)

        #set volume
        ui.set_volume(options['volume'])

        ui.debug(ui_load_timer)
        ui.debug(parse_timer)

        #check for -h,--help
        if options['help']:
            new_args = [task] + args
            task = commands.fallback_cmd
            func, options, args_left = _parse(task, new_args)

        command_timer = None
        if task not in commands.no_db:
            db_load_timer = util.Timer("Database load")
            path = os.path.join(
                cwd, options['database']) if options['database'] else cwd

            db = database.DB(path, ui=ui)
            if not db.exists():
                raise error.Abort("No Abundant database found.")
            ui.db_conf(db)
            ui.debug(db_load_timer)

            command_timer = util.Timer("Command '%s'" % task)
            ret = func(ui, db, *args_left, **options)
        else:
            command_timer = util.Timer("Command %s" % task)
            ret = func(ui, *args_left, **options)

        ui.debug(command_timer)
        ui.debug(exec_timer)

        if ret is None:
            return 0
        else:
            return ret

        # Global error handling starts here
    except error.Abort as err:
        ui.alert("Abort: ", err)
        return 2
    except error.CommandError as err:
        ui.alert("Invalid Command:\n", err)
        try:
            ui.flush()  # ensure error displays first
            exec([commands.fallback_cmd, err.task], cwd)
        except:
            # if there is no err.task then don't bother outputting help on it
            pass
        return 3

    except Exception as err:
        '''Exceptions we were not expecting.'''
        exc_type, exc_value, exc_traceback = sys.exc_info()
        sys.stderr.write(
            "Unexpected exception was raised.  This should not happen.\n")
        sys.stderr.write("Please report the entire output to Michael\n")
        sys.stderr.write("\nCommand line arguments:\n  %s\n" %
                         ' '.join(sys.argv))
        traceback.print_exception(exc_type, exc_value, exc_traceback)
        return 10
Exemplo n.º 11
0
def new(ui, db, *args, **opts):
    '''Create a new issue
    
    Creates a new open issue and, if set, marks the current user as the creator.
    Options can be used to set additional information about the issue.  See the
    update command to change/add/remove this information from an existing issue. 
    '''
    if not opts['user']:
        opts['user'] = ui.config('ui', 'username')
    if not opts['assign_to']:
        opts['assign_to'] = opts['user']

    # metadata
    try:
        metas = ['issue', 'severity', 'category']
        for meta in metas:
            try:
                if opts[meta]:
                    if db.meta_prefix[meta]:
                        opts[meta] = db.meta_prefix[meta][opts[meta]]
                else:
                    opts[meta] = ui.config('metadata', meta + '.default')
            except Exception as err:
                err.cause = meta
                raise err
    except error.UnknownPrefix as err:
        raise error.Abort("%s is not a valid option for %s" %
                          (err.prefix, err.cause))
    except error.AmbiguousPrefix as err:
        raise error.Abort("%s is an ambiguous option for %s, choices: %s" %
                          (err.prefix, err.cause, util.list2str(err.choices)))

    opts['status'] = ui.config('metadata', 'status.default')

    #construct issue
    iss = issue.Issue(
        title=(' '.join(args)).strip(),
        assigned_to=db.get_user(opts['assign_to'])
        if opts['assign_to'] else None,
        listeners=[db.get_user(i)
                   for i in opts['listener']] if opts['listener'] else None,
        issue=opts['issue'],
        severity=opts['severity'],
        category=opts['category'],
        target=opts['target'],
        parent=db.get_issue_id(opts['parent']) if opts['parent'] else None,
        creator=db.get_user(opts['user']) if opts['user'] else None)
    if opts['parent']:
        parent = db.get_issue(opts['parent'])
        parent.children.append(iss.id)
        parent.to_JSON(db.issues)

    db.iss_prefix.add(iss.id)
    iss.to_JSON(db.issues)

    if ui.volume == useri.quiet:
        ui.quiet(iss.id)
    ui.write("Created new issue with ID %s" %
             db.iss_prefix.pref_str(iss.id, True))
    skip = ['id', 'creation_date'
            ] + (['creator', 'assigned_to']
                 if db.single_user() and ui.volume < useri.verbose else [])
    ui.write(iss.descChanges(issue.base, ui, skip=skip))
Exemplo n.º 12
0
def edit(ui, db, pref, *args, **opts):
    '''Edit the content of the issue
    
    Notably the fields Paths, Description, Reproduction Steps,
    Expected Result, and Stack Trace.  An editor is launched
    prompting the user to update this data, unless any of these
    are provided at the command line, in which case the
    provided fields are overwritten without launching an editor.
    '''

    iss = db.get_issue(pref)
    origiss = db.get_issue(pref)

    if opts['paths'] or opts['description'] or opts['reproduction'] or opts[
            'expected'] or opts['trace']:
        iss.paths = opts['paths'] if opts['paths'] else iss.paths
        iss.description = opts['description'] if opts[
            'description'] else iss.description
        iss.reproduction = opts['reproduction'] if opts[
            'reproduction'] else iss.reproduction
        iss.expected = opts['expected'] if opts['expected'] else iss.expected
        iss.trace = opts['trace'] if opts['trace'] else iss.trace
    else:
        formatting = (
            ("Editing Issue %s:  %s\n\n"
             "[Paths]\n%s\n\n"
             "[Description]\n%s\n\n"
             "[Reproduction Steps]\n%s\n\n"
             "[Expected Result]\n%s\n\n"
             "[Stack Trace]\n%s") %
            (db.iss_prefix.prefix(iss.id), iss.title,
             util.list2str(iss.paths, True,
                           ''), iss.description if iss.description else '',
             iss.reproduction if iss.reproduction else '', iss.expected
             if iss.expected else '', iss.trace if iss.trace else ''))

        details = util.bracket_strip(util.ab_strip(ui.edit(formatting)))

        danger = False
        count = len(details)
        if count != 6:
            if count < 6:
                details += [''] * (6 - count)
            danger = True

        _, paths, description, reproduction, expected, trace = details[0:6]

        if danger:
            ui.alert("Warning: The details could not be parsed cleanly.\n"
                     "This is usually due to inserting or removing sections.")
            ui.alert((
                "The issue's details will be set to the following:\n"
                "Paths:\n%s\n\nDescription:\n%s\n\nReproduction Steps:\n%s\n\n"
                "Expected Results:\n%s\n\nStack Trace:\n%s") %
                     (paths, description, reproduction, expected, trace))
            safe = ui.confirm("Do you want to continue?", False, err=True)
            if not safe:
                raise error.Abort("Failed to parse the details file.")

        iss.paths = paths.splitlines()
        iss.description = description if description else None
        iss.reproduction = reproduction if reproduction else None
        iss.expected = expected if expected else None
        iss.trace = trace if trace else None

    iss.to_JSON(db.issues)

    ui.write("Updated issue %s" % db.iss_prefix.pref_str(iss.id, True))
    ui.write(iss.descChanges(origiss, ui))

    return 0