Browse Source

Move code into a class. Rename p to self.log_prefix. Move matching_issue_labels to issue['lower_labels']. Rename cmd to mode.

master
poikilos 3 years ago
parent
commit
dc76e2a9bd
  1. 424
      utilities/enissue.py

424
utilities/enissue.py

@ -34,10 +34,10 @@ def decode_safe(b):
return s return s
cmd = None
# me = sys.argv[0] # me = sys.argv[0]
me = os.path.basename(__file__) me = os.path.basename(__file__)
cmds = { modes = {
"list": { "list": {
"help": ("List all issues. Provide one or more labels to narrow" "help": ("List all issues. Provide one or more labels to narrow"
" down the list. Alternatively, provide a label only." " down the list. Alternatively, provide a label only."
@ -71,7 +71,7 @@ def usage():
left_w = 10 left_w = 10
spacer = " -- " spacer = " -- "
line_fmt = "{: <" + str(left_w) + "}" + spacer + "{}" line_fmt = "{: <" + str(left_w) + "}" + spacer + "{}"
for name, command in cmds.items(): for name, command in modes.items():
hlp = command["help"] hlp = command["help"]
print(line_fmt.format(name, hlp)) print(line_fmt.format(name, hlp))
if len(command["examples"]) > 0: if len(command["examples"]) > 0:
@ -82,92 +82,67 @@ def usage():
print("") print("")
page = None class Repo:
prev_arg = None
match_number = None
for i in range(1, len(sys.argv)):
arg = sys.argv[i]
if arg.startswith("#"):
arg = arg[1:]
if (cmd is None) and (arg in cmds.keys()):
# don't set the command to list unless the enclosing case is
# true. If a label was specified, paging is handled in the
# other case.
if arg == "page":
cmd = "list"
else:
cmd = arg
else:
try:
i = int(arg)
if prev_arg == "page":
page = i
else:
match_number = i
except ValueError:
if (cmd is None) and (cmds.get(arg) is not None):
cmd = arg
else:
if arg != "page":
# print("* adding criteria: {}".format(arg))
cmd = "list"
match_all_labels.append(arg)
prev_arg = arg
if cmd is None:
if len(match_all_labels) > 1:
cmd = "list"
if match_number is not None:
cmd = "issue"
valid_cmds = ["issue"]
for k, v in cmds.items():
valid_cmds.append(k)
if cmd is None:
print()
print()
usage()
print()
print()
exit(0)
elif cmd not in valid_cmds:
print()
print()
usage()
print()
print(cmd + " is not a valid command.")
print()
print()
exit(0)
print("")
# print("Loading...")
caches_path = "/tmp/enissue" caches_path = "/tmp/enissue"
remote_user = "poikilos"
repo_name = "EnlivenMinetest" def __init__(
c_remote_user_path = os.path.join(caches_path, remote_user) self,
c_repo_path = os.path.join(c_remote_user_path, repo_name) remote_user = "poikilos",
api_repo_url_fmt = "https://api.github.com/repos/{}/{}" repo_name = "EnlivenMinetest",
repo_url = api_repo_url_fmt.format(remote_user, repo_name) api_repo_url_fmt = "https://api.github.com/repos/{ru}/{rn}",
issues_url = repo_url + "/issues" page_size = 30,
labels_url = repo_url + "/labels" c_issues_name_fmt = "issues_page={p}{q}.json",
page_size = 30 # The GitHub API only lists 30 issues per page. ):
p = "@ " '''
Keyword arguments:
remote_user -- The repo user may be used in api_repo_url_fmt.
def get_issues(): repo_name -- The repo name may be used in api_repo_url_fmt.
query_s = issues_url api_repo_url_fmt -- a format string where {ru} is where a repo
c_issues_name_fmt = "issues_page={}{}.json" user goes (if any), and {rn} is where a
repo name goes (if any).
page_size -- This must be set to the page size that is
compatible with the URL in api_repo_url_fmt, such
as exactly 30 for GitHub.
c_issues_name_fmt -- This is the format of the URL query tail
that shows a certain page by page number,
where {p} is the page number and {q} is any
additional query such as "&label=bug".
'''
self.page = None
self.remote_user = remote_user
self.repo_name = repo_name
self.c_remote_user_path = os.path.join(Repo.caches_path,
self.remote_user)
self.c_repo_path = os.path.join(self.c_remote_user_path,
self.repo_name)
self.api_repo_url_fmt = api_repo_url_fmt
self.repo_url = self.api_repo_url_fmt.format(
ru=remote_user,
rn=repo_name
)
self.issues_url = self.repo_url + "/issues"
self.labels_url = self.repo_url + "/labels"
self.page_size = page_size
self.log_prefix = "@ "
self.c_issues_name_fmt = c_issues_name_fmt
self.label_ids = [] # all label ids in the repo
self.labels = [] # all labels in the repo
def get_issues(self):
query_s = self.issues_url
this_page = 1 this_page = 1
query_part = "" query_part = ""
# TODO: IF longer API query is implemented, put something in # TODO: IF longer API query is implemented, put something in
# query_part like "&label=Bucket_Game" # query_part like "&label=Bucket_Game"
if page is not None: if self.page is not None:
this_page = page this_page = self.page
c_issues_name = c_issues_name_fmt.format(this_page, query_part) c_issues_name = self.c_issues_name_fmt.format(p=this_page,
c_issues_path = os.path.join(c_repo_path, c_issues_name) q=query_part)
c_issues_path = os.path.join(self.c_repo_path, c_issues_name)
p = self.log_prefix
if os.path.isfile(c_issues_path): if os.path.isfile(c_issues_path):
# See <https://stackoverflow.com/questions/7430928/ # See <https://stackoverflow.com/questions/7430928/
# comparing-dates-to-check-for-old-files> # comparing-dates-to-check-for-old-files>
@ -190,16 +165,22 @@ def get_issues():
if issue_n is not None: if issue_n is not None:
if (max_issue is None) or (issue_n > max_issue): if (max_issue is None) or (issue_n > max_issue):
max_issue = issue_n max_issue = issue_n
print(p+"The highest cached issue# is {}.".format(max_issue)) print(p+"The highest cached issue# is {}.".format(
max_issue
))
return results return results
else: else:
print(p+"Cache time limit: {}".format(max_cache_delta)) print(p+"Cache time limit: {}".format(max_cache_delta))
print(p+"The cache has expired: \"{}\"".format(c_issues_path)) print(p+"The cache has expired: \"{}\"".format(
c_issues_path
))
else: else:
print(p+"There is no cache for \"{}\"".format(c_issues_path)) print(p+"There is no cache for \"{}\"".format(
c_issues_path
))
if page is not None: if self.page is not None:
query_s = issues_url + "?page=" + str(page) query_s = self.issues_url + "?page=" + str(self.page)
try: try:
response = request.urlopen(query_s) response = request.urlopen(query_s)
except urllib.error.HTTPError as e: except urllib.error.HTTPError as e:
@ -211,8 +192,8 @@ def get_issues():
print(str(e)) print(str(e))
return None return None
response_s = decode_safe(response.read()) response_s = decode_safe(response.read())
if not os.path.isdir(c_repo_path): if not os.path.isdir(self.c_repo_path):
os.makedirs(c_repo_path) os.makedirs(self.c_repo_path)
print(p+"Saving issues cache: {}".format(c_issues_path)) print(p+"Saving issues cache: {}".format(c_issues_path))
with open(c_issues_path, "w") as outs: with open(c_issues_path, "w") as outs:
outs.write(response_s) outs.write(response_s)
@ -220,67 +201,8 @@ def get_issues():
return results return results
# TODO: get labels another way, and make this conditional: def show_issue(self, issue):
# if cmd == "list": p = self.log_prefix
issues = get_issues()
if issues is None:
exit(0)
label_ids = []
labels = []
match_all_labels_lower = []
for s in match_all_labels:
# print(p+"appending"
# " {} to match_all_labels_lower.".format(s.lower()))
match_all_labels_lower.append(s.lower())
match_count = 0
total_count = len(issues)
matching_issue = None
matching_issue_labels = None
# TODO: get labels another way, and make this conditional:
# if cmd == "list":
for issue in issues:
this_issue_labels_lower = []
for label in issue["labels"]:
label_ids.append(label["id"])
if label["url"].startswith(labels_url):
start = len(labels_url) + 1 # +1 for "/"
label_encoded = label["url"][start:]
label_s = unquote(label_encoded)
this_issue_labels_lower.append(label_s.lower())
if label_s not in labels:
labels.append(label_s)
else:
raise ValueError(p+"ERROR: The url '{}' does not start with"
" '{}'".format(label["url"], labels_url))
if len(match_all_labels) > 0:
this_issue_match_count = 0
for try_label in match_all_labels_lower:
if try_label in this_issue_labels_lower:
this_issue_match_count += 1
# else:
# print(p+"{} is not a match ({} is not in"
# " {})".format(issue["number"], try_label,
# this_issue_labels_lower))
if this_issue_match_count == len(match_all_labels):
match_count += 1
print("#{} {}".format(issue["number"], issue["title"]))
elif (match_number is None) and (cmd == "list"):
# Show all since no criteria is set.
match_count += 1
print("#{} {}".format(issue["number"], issue["title"]))
if match_number is not None:
# INFO: match_number & issue["number"] are ints
if match_number == issue["number"]:
matching_issue = issue
matching_issue_labels = this_issue_labels_lower
issue = None # prevent value from leaking
def show_issue(issue):
print("") print("")
print("#{} {}".format(issue["number"], issue["title"])) print("#{} {}".format(issue["number"], issue["title"]))
# print(issue["html_url"]) # print(issue["html_url"])
@ -305,7 +227,10 @@ def show_issue(issue):
markdown = issue_data.get("body") markdown = issue_data.get("body")
# ^ It is (always?) present but allowed to be None by GitHub! # ^ It is (always?) present but allowed to be None by GitHub!
if markdown is not None: if markdown is not None:
markdown = markdown.replace("\\r\\n", "\n").replace("\\t", "\t") markdown = markdown.replace("\\r\\n", "\n").replace(
"\\t",
"\t"
)
left_w = 10 left_w = 10
spacer = " " spacer = " "
line_fmt = "{: <" + str(left_w) + "}" + spacer + "{}" line_fmt = "{: <" + str(left_w) + "}" + spacer + "{}"
@ -315,14 +240,15 @@ def show_issue(issue):
assignees = issue_data.get("assignees") assignees = issue_data.get("assignees")
if (assignees is not None) and len(assignees) > 1: if (assignees is not None) and len(assignees) > 1:
assignee_names = [a["login"] for a in assignees] assignee_names = [a["login"] for a in assignees]
print(line_fmt.format("assignees:", " ".join(assignee_names))) print(line_fmt.format("assignees:",
" ".join(assignee_names)))
elif issue_data.get("assignee") is not None: elif issue_data.get("assignee") is not None:
assignee_name = issue_data["assignee"]["login"] assignee_name = issue_data["assignee"]["login"]
print(line_fmt.format("assignee:", assignee_name)) print(line_fmt.format("assignee:", assignee_name))
labels_s = "None" labels_s = "None"
if len(matching_issue_labels) > 0: if len(issue['lower_labels']) > 0:
neat_labels = [] neat_labels = []
for label_s in matching_issue_labels: for label_s in issue['lower_labels']:
if " " in label_s: if " " in label_s:
neat_labels.append('"' + label_s + '"') neat_labels.append('"' + label_s + '"')
else: else:
@ -345,13 +271,14 @@ def show_issue(issue):
cmts_data_s = decode_safe(response.read()) cmts_data_s = decode_safe(response.read())
cmts_data = json.loads(cmts_data_s) cmts_data = json.loads(cmts_data_s)
left_margin = " " left_margin = " "
c_prop_fmt = (left_margin + "{: <" + str(left_w) + "}" + spacer c_prop_fmt = (left_margin + "{: <" + str(left_w) + "}"
+ "{}") + spacer + "{}")
for cmt in cmts_data: for cmt in cmts_data:
print("") print("")
print("") print("")
print(c_prop_fmt.format("from:", cmt["user"]["login"])) print(c_prop_fmt.format("from:", cmt["user"]["login"]))
print(c_prop_fmt.format("updated_at:", cmt["updated_at"])) print(c_prop_fmt.format("updated_at:",
cmt["updated_at"]))
print("") print("")
print(left_margin + '"""') print(left_margin + '"""')
print(left_margin + cmt["body"]) print(left_margin + cmt["body"])
@ -362,61 +289,206 @@ def show_issue(issue):
print("") print("")
return True return True
def load_issues(self):
self.issues = self.get_issues()
def get_match(self, mode, match_number=None, match_all_labels_lower=[]):
'''
Sequential arguments:
mode -- This must be a valid mode
(a key in the modes dictionary).
Keyword arguments:
match_number -- Match this issue number (None for multiple).
match_all_labels_lower -- Only match where all of these labels
are on the issue.
'''
matching_issue = None
match_count = 0
# TODO: get labels another way, and make this conditional:
# if mode == "list":
for issue in self.issues:
this_issue_labels_lower = []
for label in issue["labels"]:
self.label_ids.append(label["id"])
if label["url"].startswith(self.labels_url):
start = len(self.labels_url) + 1 # +1 for "/"
label_encoded = label["url"][start:]
label_s = unquote(label_encoded)
this_issue_labels_lower.append(label_s.lower())
if label_s not in self.labels:
self.labels.append(label_s)
else:
raise ValueError(p+"ERROR: The url '{}' does not"
" start with '{}'"
"".format(label["url"],
self.labels_url))
if len(match_all_labels) > 0:
this_issue_match_count = 0
for try_label in match_all_labels_lower:
if try_label in this_issue_labels_lower:
this_issue_match_count += 1
# else:
# print(p+"{} is not a match ({} is not in"
# " {})".format(issue["number"], try_label,
# this_issue_labels_lower))
if this_issue_match_count == len(match_all_labels):
match_count += 1
print("#{} {}".format(issue["number"], issue["title"]))
elif (match_number is None) and (mode == "list"):
# Show all since no criteria is set.
match_count += 1
print("#{} {}".format(issue["number"], issue["title"]))
if match_number is not None:
# INFO: match_number & issue["number"] are ints
if match_number == issue["number"]:
matching_issue = issue
issue['lower_labels'] = this_issue_labels_lower
return {'issue':matching_issue, 'count':match_count}
def main():
mode = None
repo = Repo()
prev_arg = None
match_number = None
for i in range(1, len(sys.argv)):
arg = sys.argv[i]
if arg.startswith("#"):
arg = arg[1:]
if (mode is None) and (arg in modes.keys()):
# don't set the command to list unless the enclosing case is
# true. If a label was specified, paging is handled in the
# other case.
if arg == "page":
mode = "list"
else:
mode = arg
else:
try:
i = int(arg)
if prev_arg == "page":
repo.page = i
else:
match_number = i
except ValueError:
if (mode is None) and (modes.get(arg) is not None):
mode = arg
else:
if arg != "page":
# print("* adding criteria: {}".format(arg))
mode = "list"
match_all_labels.append(arg)
prev_arg = arg
if mode is None:
if len(match_all_labels) > 1:
mode = "list"
if match_number is not None:
mode = "issue"
valid_modes = ["issue"]
for k, v in modes.items():
valid_modes.append(k)
if mode is None:
print()
print()
usage()
print()
print()
sys.exit(0)
elif mode not in valid_modes:
print()
print()
usage()
print()
print(mode + " is not a valid command.")
print()
print()
sys.exit(0)
print("")
# print("Loading...")
# TODO: get labels another way, and make this conditional:
# if mode == "list":
repo.load_issues()
if repo.issues is None:
print("There were no issues.")
sys.exit(0)
match_all_labels_lower = []
p = repo.log_prefix
for s in match_all_labels:
# print(p+"appending"
# " {} to match_all_labels_lower.".format(s.lower()))
match_all_labels_lower.append(s.lower())
total_count = len(repo.issues)
match = repo.get_match(
mode,
match_number=match_number,
match_all_labels_lower=match_all_labels_lower,
)
matching_issue = match['issue']
if matching_issue is not None: if matching_issue is not None:
show_issue(matching_issue) repo.show_issue(matching_issue)
if cmd == "labels": if mode == "labels":
# print("Labels:") # print("Labels:")
# print("") # print("")
for label_s in labels: for label_s in repo.labels:
print(label_s) print(label_s)
print("") print("")
print("The repo has {} label(s).".format(len(labels))) print("The repo has {} label(s).".format(len(repo.labels)))
print("") print("")
if total_count >= page_size: if total_count >= repo.page_size:
print("The maximum issue count per page was reached.") print("The maximum issue count per page was reached.")
next_page = 2 next_page = 2
if page is not None: if repo.page is not None:
next_page = page + 1 next_page = repo.page + 1
print(" ./" + me + " labels page " + str(next_page)) print(" ./" + me + " labels page " + str(next_page))
print("to see labels on additional pages.") print("to see labels on additional pages.")
elif cmd == "list": elif mode == "list":
print() print()
if len(match_all_labels) > 0: if len(match_all_labels) > 0:
print("{} issue(s) matched {}".format( print("{} issue(s) matched {}".format(
match_count, match['count'],
" + ".join("'{}'".format(s) for s in match_all_labels) " + ".join("'{}'".format(s) for s in match_all_labels)
)) ))
if total_count >= page_size: if total_count >= repo.page_size:
print("{} searched, which is the maximum number" print("{} searched, which is the maximum number"
" per page.".format(total_count)) " per page.".format(total_count))
next_page = 2 next_page = 2
if page is not None: if repo.page is not None:
next_page = page + 1 next_page = repo.page + 1
print(" ./" + me + " " + " ".join(match_all_labels) print(" ./" + me + " " + " ".join(match_all_labels)
+ " page " + str(next_page)) + " page " + str(next_page))
print("to see additional pages.") print("to see additional pages.")
else: else:
if page is not None: if repo.page is not None:
print("{} issue(s) are showing for page" print("{} issue(s) are showing for page"
" {}.".format(match_count, page)) " {}.".format(match['count'], repo.page))
else: else:
print("{} issue(s) are showing.".format(match_count)) print("{} issue(s) are showing.".format(match['count']))
if total_count >= page_size: if total_count >= repo.page_size:
print("That is the maximum number per page. Type") print("That is the maximum number per page. Type")
next_page = 2 next_page = 2
if page is not None: if repo.page is not None:
next_page = page + 1 next_page = repo.page + 1
print(" ./" + me + " page " + str(next_page)) print(" ./" + me + " page " + str(next_page))
print("to see additional pages.") print("to see additional pages.")
if match_count > 0: if match['count'] > 0:
print() print()
print() print()
print("To view details, type") print("To view details, type")
print(" ./" + me) print(" ./" + me)
print("followed by a number.") print("followed by a number.")
print("") print("")
if __name__ == "__main__":
main()

Loading…
Cancel
Save