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 the local keyring? [y/n]:" first_run = ( not( config.base_url or config.username or keyring.get_password('jira-cli',username) ) ) if persist or first_run: config.base_url = url config.save() keyring.set_password('jira-cli',username,password) 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 keyring.set_password('jira-cli',username,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 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 the local keyring? [y/n]:" first_run = (not (config.base_url or config.username or keyring.get_password('jira-cli', username))) if persist or first_run: config.base_url = url config.save() keyring.set_password('jira-cli', username, password) 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 keyring.set_password('jira-cli', username, 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 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 eval(self): if self.args.search_freetext: issues = self.jira.search_issues(self.args.search_freetext, project=self.args.project) elif self.args.search_jql: issues = self.jira.search_issues_jql(self.args.search_jql) elif self.args.filter: issues = self.jira.get_issues_by_filter(*self.args.filter) else: issues = [ issue for issue in [self.jira.get_issue(jira) for jira in self.args.jira_ids] if issue is not None ] for issue in issues: # get the time estimate values if "student" in issue["labels"]: print_output(u"{} is a student task, skipping...\n".format( issue["key"])) continue estimate = self.spent_time_or_estimate(issue, quiet=True) if estimate is None: print_error(u"{} has no time estimate\n".format(issue["key"]), severity=WARNING) continue # get the epic of this issue (warning if none) bookkeeping_story = self.get_parent_story(issue) if bookkeeping_story is None: print_error(u"{} has no parent story. Assignee: {}\n".format( issue["key"], issue["assignee"]), severity=WARNING) continue # check if the issue in question was already mentioned in the comments if self.issue_already_substracted(issue, bookkeeping_story): print_output( u"{} already mentioned in the comments of {}\n".format( issue["key"], bookkeeping_story["key"])) else: self.adjust_story_timetracking( bookkeeping_story, issue, dry=self.args.dry, verbose=(self.args.verbosity > 0))
def spent_time_or_estimate(self, issue, quiet=False): """This function will return the time spent on an issue, when the time is less or equal to the original estimate, otherwise the original estimate is returned. We now want to run this function after the sprint has ended, thus an issue without a timelog will issue a warning. returns [orininal, remaning]""" if "aggregatetimespent" not in issue or issue[ "aggregatetimespent"] == 0: if not quiet: print_error(u"No time was logged on {}\n".format(issue["key"])) return None if "timeoriginalestimate" not in issue or issue[ "timeoriginalestimate"] == 0: if not quiet: print_error(u"Missing time estimate for {}\n".format( issue["key"])) return None estimate = issue["timeoriginalestimate"] logged = issue["aggregatetimespent"] if not quiet and logged > estimate: print_error(u"Attention: {} was overbooked by {}\n".format( issue["key"], self.secs_to_human_readable(logged - estimate)), severity=WARNING) return min(estimate, logged)
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) 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 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 ) 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 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)
def initialize(config, base_url=None, username=None, password=None, persist=True, error=False, protocol='soap', reset=False): url = base_url or config.base_url auth_method = config.auth_method 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: ") if not auth_method: auth_method = prompt( "Auth method for jira(auth, basic_auth, oauth). More details: https://jira.readthedocs.io/en/master/examples.html#authentication: " ) config.auth_method = auth_method config.save() if auth_method in ("auth", "basic_auth"): username = (username or (not error and config.username) or prompt("username: "******"password: "******"oauth": access_token = ((not error and config.oauth_access_token) or prompt("OAuth access token: ")) access_token_secret = (( not error and (not reset and keyring.get_password('jira-cli', access_token))) or prompt("OAuth access token secret: ", True)) consumer_key = ((not error and config.oauth_consumer_key) or prompt("OAuth consumer key: ")) key_cert_file = ((not error and config.oauth_key_cert_file) or prompt("OAuth key/cert filename: ")) with open(key_cert_file, 'r') as f: key_cert_data = f.read() auth_kwargs = { auth_method: { 'access_token': access_token, 'access_token_secret': access_token_secret, 'consumer_key': consumer_key, 'key_cert': key_cert_data, } } else: raise NotImplementedError("Unknown auth method: %s" % auth_method) jira = not error and bridge or get_bridge(protocol)(url, config, persist) persist_warning = "would you like to persist the credentials to the local keyring? [y/n]:" if auth_method in ("auth", "basic_auth"): first_run = (not (config.base_url or config.username or keyring.get_password('jira-cli', username))) if persist or first_run: config.base_url = url config.save() keyring.set_password('jira-cli', username, password) elif auth_method == "oauth": first_run = (not (config.base_url or config.access_token or keyring.get_password('jira-cli', access_token))) if persist or first_run: config.base_url = url config.save() keyring.set_password('jira-cli', access_token, access_token_secret) else: raise NotImplementedError("Unknown auth method: %s" % auth_method) try: jira.login(**auth_kwargs) 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) if (auth_method in ("auth", "basic_auth") and (persist or first_run) and (not (config.username == username or config.password == password)) and "y" == prompt(persist_warning)): config.username = username keyring.set_password('jira-cli', username, password) config.save() elif (auth_method == "oauth" and (persist or first_run) and (not config.oauth_access_token == access_token) and "y" == prompt(persist_warning)): config.oauth_access_token = access_token config.oauth_consumer_key = consumer_key config.oauth_key_cert_file = key_cert_file keyring.set_password('jira-cli', access_token, access_token_secret) config.save() config.save() return jira else: return bridge
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)
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)