def eval(self): mappers = { "issue_types": (self.jira.get_issue_types, ), 'subtask_types': (self.jira.get_subtask_issue_types, ), 'projects': (self.jira.get_projects, ), 'priorities': (self.jira.get_priorities, ), 'statuses': (self.jira.get_statuses, ), 'resolutions': (self.jira.get_resolutions, ), 'components': (self.jira.get_components, 'project'), 'versions': (self.jira.list_versions, 'project'), 'transitions': (self.jira.get_available_transitions, 'issue'), 'filters': (self.jira.get_filters, ), 'aliases': (lambda: [{ "name": k, "description": v } for k, v in list(Config(section='alias').items()).items()], ) } func, arguments = mappers[self.args.type][0], mappers[ self.args.type][1:] _ = [] _k = {} for earg in arguments: if isinstance(earg, tuple): if getattr(self.args, earg[0]): _k.update({earg[0]: getattr(self.args, earg[0])}) else: _k[earg[0]] = earg[1] else: if not getattr(self.args, earg): raise UsageError("'--%s' is required for listing '%s'" % (earg, self.args.type)) _.append(getattr(self.args, earg)) found = False data = func(*_, **_k) data_dict = OrderedDict() if type(data) == type([]): for item in data: data_dict[item['name']] = item else: data_dict = data for item in list(data_dict.values()): found = True val = item if type(item) == type({}): val = colorfunc(item['name'], 'white') if 'key' in item and item['key']: val += " [" + colorfunc(item['key'], 'magenta') + "]" if 'description' in item and item['description']: val += " [" + colorfunc(item['description'], 'green') + "]" print_output(colorfunc(val, 'white')) if not found: raise UsageWarning("No %s found." % self.args.type)
def adjust_story_timetracking(self, story, issue, dry=True, verbose=True): timetracking = story["timetracking"] estimate = self.spent_time_or_estimate(issue) estimate_human_readable = self.secs_to_human_readable(estimate) issue = self.jira.get_issue(issue["key"], raw=True) message = str(u"{}: {}: reduced by {}".format( issue.fields.assignee.displayName, issue.key, estimate_human_readable)) new_original_raw = timetracking.originalEstimateSeconds - estimate new_remaining_raw = timetracking.remainingEstimateSeconds - estimate if new_original_raw < 0 or new_remaining_raw < 0: print_error( u"Story {} full. Estimate would become negative, skipping\n". format(story["key"]), severity=WARNING) return new_original = self.secs_to_human_readable(new_original_raw) new_remaining = self.secs_to_human_readable(new_remaining_raw) if verbose: msg = u"Adjusting estimate of story [{}]: {}".format( story["key"], story["summary"]) msg += u"\nby issue [{}]: {}".format(issue.key, issue.fields.summary) print_output(colorfunc(msg, "blue")) print_output(u"{}: {} - {} = {}".format( colorfunc("Original Estimate", "white"), timetracking.originalEstimate, estimate_human_readable, new_original)) print_output(u"{}: {} - {} = {}".format( colorfunc("Remaning Estimate", "white"), timetracking.remainingEstimate, estimate_human_readable, new_remaining)) print_output("comment: {}".format(colorfunc(message, "white"))) print("") if not dry: # fields = { # "timetracking": {"originalEstimate": "3w 2d 1h", "remainingEstimate": "3w 2d 1h"}} story = self.jira.get_issue(story["key"], raw=True) story.update( fields={ "timetracking": { "originalEstimate": new_original, "remainingEstimate": new_remaining } }) self.jira.add_comment(story, message)
def cli(args=sys.argv[1:]): import optparse alias_config = Config(section='alias') if set(list(alias_config.items().keys())).intersection(args): for alias, target in list(alias_config.items()).items(): if args[0] == alias: args = shlex.split(target) + args[1:] break parser = build_parser() try: config = Config() pre_opts, pre_args = None, None try: optparser = optparse.OptionParser() def void(*args): raise SystemExit() optparser.print_usage = void optparser.add_option("", "--version", action='store_true', default=False) pre_opts, pre_args = optparser.parse_args(args) except SystemExit: pass if pre_opts and pre_opts.version: print(__version__) return if not (pre_opts and ("configure" in pre_args or "clear_cache" in pre_args)): post_args = parser.parse_args(args) jira = initialize( config, post_args.jira_url, post_args.username, post_args.password, persist=not (post_args.username or post_args.jira_url), protocol='rest' ) return post_args.cmd(jira, post_args).execute() else: if "configure" in pre_args: config.reset() initialize( config, "", "", "", True, protocol='rest' ) elif "clear_cache" in pre_args: clear_cache() print_output(colorfunc("jira-cli cache cleared", "green")) return except KeyboardInterrupt: print_error("aborted", severity=WARNING) except UsageWarning as e: print_error(str(e), severity=WARNING) except (JiraCliError, UsageError) as e: print_error(str(e)) except (WebFault) as e: print_error(JiraCliError(e)) except (JIRAError) as e: print_error(JiraCliError(e)) except NotImplementedError as e: print_error(e)
def cli(args=sys.argv[1:]): parser = build_parser() try: config = Config() pre_opts, pre_args = None, None try: pre_opts, pre_args = fake_parse(args) except StopIteration: pre_opts, pre_args = None, None if "--v1" in args or config.v1: if '--v1' in sys.argv: print_error( "Use of the v1 interface is no longer supported. Please refer to jiracli.readthedocs.io", WARNING ) sys.argv.remove("--v1") return old_main() except SystemExit: pass if pre_opts and pre_opts.version: print(__version__) return if ( not (pre_opts or pre_args) or (pre_opts and not (pre_opts.v1 or config.v1)) and not (pre_opts and ("configure" in pre_args or "clear_cache" in pre_args)) ): post_args = parser.parse_args(args) jira = initialize( config, post_args.jira_url, post_args.username, post_args.password, persist=not (post_args.username or post_args.jira_url), protocol=post_args.protocol or config.protocol or 'rest' ) return post_args.cmd(jira, post_args).execute() else: if "configure" in pre_args: config.reset() initialize( config, "", "", "", True, protocol=pre_opts.protocol or config.protocol or 'soap' ) elif "clear_cache" in pre_args: clear_cache() print_output(colorfunc("jira-cli cache cleared", "green")) return except KeyboardInterrupt: print_error("aborted", severity=WARNING) except UsageWarning as e: print_error(str(e), severity=WARNING) except (JiraCliError, UsageError) as e: print_error(str(e)) except (WebFault) as e: print_error(JiraCliError(e)) except (JIRAError) as e: print_error(JiraCliError(e)) except NotImplementedError as e: print_error(e)
def initialize(config, base_url=None, username=None, password=None, persist=True, error=False, protocol='soap'): url = base_url or config.base_url bridge = get_bridge(protocol)(url, config, persist) if (url and not error) else None if error or not (url and bridge and bridge.ping()): url = url or prompt("Base url for the jira instance: ") username = ( username or (not error and config.username) or prompt("username: "******"password: "******"would you like to persist the credentials to ~/.jira-cli/config.cfg?\n{0} [y/n]:" persist_warning = persist_warning.format(colorfunc('[WARNING: this will ' 'store credentials in plaintext', 'red')) first_run = ( not( config.base_url or config.username or config.password ) ) if persist or first_run: config.base_url = url config.save() try: jira.login(username, password) if ( (persist or first_run) and ( not ( config.username == username or config.password == password ) ) and "y" == prompt(persist_warning) ): config.username = username config.password = password config.save() config.save() return jira except JiraAuthenticationError: print_error("invalid username/password", severity=WARNING) return initialize(config, base_url=url, error=True, protocol=protocol, persist=persist) except JiraInitializationError: print_error("invalid jira location", severity=WARNING) config.base_url = "" return initialize(config, error=True, protocol=protocol, persist=persist) else: return bridge
def cli(args=sys.argv[1:]): parser = build_parser() try: config = Config() pre_opts, pre_args = None, None try: pre_opts, pre_args = fake_parse(args) except StopIteration: pre_opts, pre_args = None, None if not ("--v2" in args or config.v2): return old_main() except SystemExit: pass if pre_opts and pre_opts.version: print(__version__) return if (not (pre_opts or pre_args) or (pre_opts and (pre_opts.v2 or config.v2)) and not (pre_opts and ("configure" in pre_args or "clear_cache" in pre_args))): post_args = parser.parse_args(args) jira = initialize( config, post_args.jira_url, post_args.username, post_args.password, persist=not (post_args.username or post_args.jira_url), protocol=post_args.protocol or config.protocol or 'soap') return post_args.cmd(jira, post_args).execute() else: if "configure" in pre_args: config.reset() initialize(config, "", "", "", True, protocol=pre_opts.protocol or config.protocol or 'soap') elif "clear_cache" in pre_args: clear_cache() print_output(colorfunc("jira-cli cache cleared", "green")) return except KeyboardInterrupt: print_error("aborted", severity=WARNING) except UsageWarning as e: print_error(str(e), severity=WARNING) except (JiraCliError, UsageError) as e: print_error(str(e)) except (WebFault) as e: print_error(JiraCliError(e)) except (JIRAError) as e: print_error(JiraCliError(e)) except NotImplementedError as e: print_error(e)
def initialize(config, base_url=None, username=None, password=None, persist=True, error=False, protocol='soap'): url = base_url or config.base_url bridge = get_bridge(protocol)(url, config, persist) if (url and not error) else None if error or not (url and bridge and bridge.ping()): url = url or prompt("Base url for the jira instance: ") username = (username or (not error and config.username) or prompt("username: "******"password: "******"would you like to persist the credentials to ~/.jira-cli/config.cfg?\n{0} [y/n]:" persist_warning = persist_warning.format( colorfunc('[WARNING: this will ' 'store credentials in plaintext', 'red')) first_run = (not (config.base_url or config.username or config.password)) if persist or first_run: config.base_url = url config.save() try: jira.login(username, password) if ((persist or first_run) and (not (config.username == username or config.password == password)) and "y" == prompt(persist_warning)): config.username = username config.password = password config.save() config.save() return jira except JiraAuthenticationError: print_error("invalid username/password", severity=WARNING) return initialize(config, base_url=url, error=True, protocol=protocol, persist=persist) except JiraInitializationError: print_error("invalid jira location", severity=WARNING) config.base_url = "" return initialize(config, error=True, protocol=protocol, persist=persist) else: return bridge
def show_work_log(self): issue = self.jira.get_issue(self.args.jira_id) if not issue: return if "timetracking" in issue and hasattr(issue["timetracking"], "originalEstimate"): print_output("{}: {}".format( colorfunc("Estimated", "white"), colorfunc(issue["timetracking"].originalEstimate, "blue"))) print_output("{}: {}".format( colorfunc("Remaining", "white"), colorfunc(issue["timetracking"].remainingEstimate, "blue"))) time_spent_seconds = getattr(issue["timetracking"], "timeSpentSeconds", 0) if time_spent_seconds <= issue[ "timetracking"].originalEstimateSeconds: color = "green" else: color = "red" print_output("{}: {}".format( colorfunc("Logged ", "white"), colorfunc( getattr(issue["timetracking"], "timeSpent", str(time_spent_seconds) + "m"), color))) print_output("") worklogs = issue["worklog"].worklogs for worklog in worklogs: print_output(self.format_worklog(worklog))
def cli(args=sys.argv[1:]): parser = build_parser() try: config = Config() pre_opts, pre_args = None, None try: pre_opts, pre_args = fake_parse(args) except StopIteration: pre_opts, pre_args = None, None if not ("--v2" in args or config.v2): return old_main() except SystemExit: pass if ( not (pre_opts or pre_args) or (pre_opts and pre_opts.v2) and not (pre_opts and ("configure" in pre_args or "clear_cache" in pre_args)) ): post_args = parser.parse_args(args) jira = initialize( config, post_args.jira_url, post_args.username, post_args.password, persist=not (post_args.username or post_args.jira_url), protocol=post_args.protocol or config.protocol ) return post_args.cmd(jira, post_args).execute() else: if "configure" in pre_args: config.reset() initialize(config, "", "", "", True) elif "clear_cache" in pre_args: clear_cache() print_output(colorfunc("jira-cli cache cleared", "green")) return except KeyboardInterrupt: print_error("aborted", severity=WARNING) except UsageWarning as e: print_error(str(e), severity=WARNING) except (JiraCliError, UsageError) as e: print_error(str(e)) except (WebFault) as e: print_error(JiraCliError(e)) except (JIRAError) as e: print_error(JiraCliError(e)) except NotImplementedError as e: print_error(e)
def eval(self): if self.args.extra_fields: extras = self.extract_extras() self.jira.update_issue(self.args.issue, **extras) if self.args.issue_comment: self.jira.add_comment( self.args.issue, self.args.issue_comment if isinstance( self.args.issue_comment, basestring) else get_text_from_editor()) print_output( self.jira.format_issue(self.jira.get_issue(self.args.issue), comments_only=True)) elif self.args.issue_priority: self.jira.update_issue(self.args.issue, priority=self.jira.get_priorities()[ self.args.issue_priority]["id"]) elif self.args.issue_components: components = dict((k["name"], k["id"]) for k in self.jira.get_components( self.args.issue.split("-")[0])) current_components = set( k["name"] for k in self.jira.get_issue(self.args.issue)["components"]) if not set( self.args.issue_components).issubset(current_components): new_components = current_components.union( self.args.issue_components) self.jira.update_issue( self.args.issue, components=[components[k] for k in new_components]) print_output( colorfunc( 'component(s): %s added to %s' % (",".join( self.args.issue_components), self.args.issue), 'green')) else: raise UsageWarning( "component(s):[%s] already exist in %s" % (",".join(self.args.issue_components), self.args.issue)) elif self.args.issue_transition: self.jira.transition_issue(self.args.issue, self.args.issue_transition.lower(), self.args.resolution) print_output( colorfunc( '%s transitioned to "%s"' % (self.args.issue, self.args.issue_transition), 'green')) elif self.args.issue_assignee: self.jira.assign_issue(self.args.issue, self.args.issue_assignee) print_output( colorfunc( '%s assigned to %s' % (self.args.issue, self.args.issue_assignee), 'green')) elif self.args.labels: self.jira.add_labels(self.args.issue, self.args.labels, True) print_output( colorfunc( '%s labelled with %s' % (self.args.issue, ",".join(self.args.labels)), 'green')) if self.args.affects_version: self.jira.add_versions(self.args.issue, self.args.affects_version, 'affects') print_output( colorfunc( 'Added affected version(s) %s to %s' % (",".join(self.args.affects_version), self.args.issue), 'green')) if self.args.remove_affects_version: self.jira.remove_versions(self.args.issue, self.args.remove_affects_version, 'affects') print_output( colorfunc( 'Removed affected version(s) %s from %s' % (",".join( self.args.remove_affects_version), self.args.issue), 'blue')) if self.args.fix_version: self.jira.add_versions(self.args.issue, self.args.fix_version, 'fix') print_output( colorfunc( 'Added fixed version(s) %s to %s' % (",".join(self.args.fix_version), self.args.issue), 'green')) if self.args.remove_fix_version: self.jira.remove_versions(self.args.issue, self.args.remove_fix_version, 'fix') print_output( colorfunc( 'Removed fixed version(s) %s from %s' % (",".join(self.args.remove_fix_version), self.args.issue), 'blue'))
def format_worklog(self, worklog): return "%s %s : %s %s" % ( colorfunc(worklog.created, "blue"), colorfunc(worklog.author, "white"), worklog.comment, colorfunc("[" + worklog.timeSpent + "]", "green"))
def format_issue(self, issue, mode=0, formatter=None, comments_only=False): fields = {} status_color = "blue" status_string = JiraBridge.object_from_key( issue.setdefault('status', '1'), self.get_statuses)["name"] if status_string.lower() in ["resolved", "closed", "done"]: status_color = "green" elif status_string.lower() in [ "open", "unassigned", "reopened", "to do" ]: status_color = "red" list_fields = set(['versions', 'fixversions']) special_fields = { "status": self.get_statuses, "priority": self.get_priorities, "type": lambda: dict( list(self.get_issue_types().items()) + list( self.get_subtask_issue_types().items())) } if formatter: groups = re.compile("(%([\w]+))").findall(formatter) ret_str = formatter.encode('utf-8') for k, v in groups: if v.lower() in list(special_fields.keys()): key = issue[v.lower()] data = "" or JiraBridge.object_from_key( key, special_fields[v.lower()])["name"] ret_str = ret_str.replace(k, data) elif v.lower() in list_fields: fix_versions = ", ".join( v.name for v in issue.get('fixVersions', [])) ret_str = ret_str.replace(k, fix_versions.encode('utf-8')) else: ret_str = ret_str.replace(k, issue.setdefault( v.lower(), "")).encode('utf-8') return ret_str if mode >= 0: # minimal fields["issue"] = issue["key"] fields["status"] = colorfunc( JiraBridge.object_from_key(issue["status"], self.get_statuses)["name"], status_color) fields["reporter"] = issue.setdefault("reporter", "") fields["assignee"] = issue.setdefault("assignee", "") fields["summary"] = issue.setdefault("summary", "") fields["link"] = colorfunc("%s/browse/%s" % (self.base_url, issue["key"]), "white", attrs=["underline"]) if mode == 1 or comments_only: fields["description"] = issue.setdefault("description", "") or "" if not issue.get("priority", ""): fields["priority"] = "" else: fields["priority"] = JiraBridge.object_from_key( issue["priority"], self.get_priorities)["name"] fields["type"] = JiraBridge.object_from_key( issue["type"], self.get_issue_types if 'parent' not in issue else self.get_subtask_issue_types)["name"] fields["comments"] = "\n" comments = self.get_issue_comments(issue["key"]) for comment in comments: comment_str = comment["body"].strip() fields["comments"] += "%s %s : %s\n" % ( colorfunc(comment["created"], "blue"), colorfunc(comment["author"], "green"), comment_str) children_string = "" if mode > 1: description = (issue.setdefault("description", "") or "").split("\n") fields["description"] = "\n".join( [description[0]] + [" " * 23 + k for k in description[1:]]) for child in self.search_issues_jql("parent=%s" % issue["key"]): child_type = JiraBridge.object_from_key( child["type"], self.get_subtask_issue_types)["name"].lower() key = ("%s" % child_type).ljust(20) value = "%s (%s) %s" % (child["key"], child["summary"], colorfunc( "%s/browse/%s" % (self.base_url, child["key"]), "white", attrs=['underline'])) children_string += "%s : %s\n" % (key, value) if comments_only: return fields["comments"].strip() elif mode < 0: url_str = colorfunc(parse.urljoin(self.base_url, "/browse/%s" % (issue["key"])), "white", attrs=["underline"]) ret_str = colorfunc(issue["key"], status_color) + " " + issue.setdefault( "summary", "") + " " + url_str if not COLOR: ret_str += " [%s] " % status_string return ret_str for k, v in list(fields.items()): if not v: fields[k] = "" formatted = "\n".join(" : ".join((k.ljust(20), v)) for k, v in list(fields.items()) if not k == 'comments') + "\n" formatted += children_string if "comments" in fields: formatted += fields["comments"] return formatted
def cli(args=sys.argv[1:]): alias_config = Config(section='alias') if set(alias_config.items().keys()).intersection(args): for alias, target in alias_config.items().items(): if args[0] == alias: args = shlex.split(target) + args[1:] break parser = build_parser() try: config = Config() pre_opts, pre_args = None, None try: pre_opts, pre_args = fake_parse(args) except StopIteration: pre_opts, pre_args = None, None if "--v1" in args or config.v1: if '--v1' in sys.argv: print_error( "Use of the v1 interface is no longer supported. Please refer to jiracli.readthedocs.io", WARNING) sys.argv.remove("--v1") return old_main() except SystemExit: pass if pre_opts and pre_opts.version: print(__version__) return if (not (pre_opts or pre_args) or (pre_opts and not (pre_opts.v1 or config.v1)) and not (pre_opts and ("configure" in pre_args or "clear_cache" in pre_args))): post_args = parser.parse_args(args) jira = initialize( config, post_args.jira_url, post_args.username, post_args.password, persist=not (post_args.username or post_args.jira_url), protocol=post_args.protocol or config.protocol or 'rest') return post_args.cmd(jira, post_args).execute() else: if "configure" in pre_args: config.reset() initialize(config, "", "", "", True, protocol=pre_opts.protocol or config.protocol or 'soap') elif "clear_cache" in pre_args: clear_cache() print_output(colorfunc("jira-cli cache cleared", "green")) return except KeyboardInterrupt: print_error("aborted", severity=WARNING) except UsageWarning as e: print_error(str(e), severity=WARNING) except (JiraCliError, UsageError) as e: print_error(str(e)) except (WebFault) as e: print_error(JiraCliError(e)) except (JIRAError) as e: print_error(JiraCliError(e)) except NotImplementedError as e: print_error(e)