Browse Source

Load subcomponents of issues.

master
poikilos 3 years ago
parent
commit
d459267ba3
  1. 190
      utilities/enissue.py
  2. 11
      utilities/pyissuesyncd

190
utilities/enissue.py

@ -227,8 +227,11 @@ def tests():
raise AssertionError("{} is None".format(k)) raise AssertionError("{} is None".format(k))
def debug(msg): def debug(*args, **kwargs):
msg = ""
if verbose: if verbose:
if len(args) > 0:
msg = args[0]
error("[debug] " + msg) error("[debug] " + msg)
def set_verbose(on): def set_verbose(on):
@ -602,7 +605,7 @@ class Repo:
self.default_query = options['default_query'] self.default_query = options['default_query']
self.hide_events = options['hide_events'] self.hide_events = options['hide_events']
self.issues = None self.issues = None
self.last_query_s = None self.last_url = None
self.options = copy.deepcopy(options) self.options = copy.deepcopy(options)
def getKnownKey(self, name): def getKnownKey(self, name):
@ -765,7 +768,7 @@ class Repo:
" only one issue because a single" " only one issue because a single"
" issue has its own URL with only" " issue has its own URL with only"
" one result (not a list).") " one result (not a list).")
query_s = self.issues_url # changed below if issue_no url = self.issues_url # changed below if issue_no
this_page = 1 this_page = 1
query_part = "" query_part = ""
@ -775,7 +778,7 @@ class Repo:
for k,v in query.items(): for k,v in query.items():
if v is not None: if v is not None:
# <https://stackoverflow.com/a/9345102/4541104>: # <https://stackoverflow.com/a/9345102/4541104>:
#query_s += ( #url += (
# "&{}={}".format(quote_plus(k), quote_plus(v)) # "&{}={}".format(quote_plus(k), quote_plus(v))
#) #)
# #
@ -813,48 +816,49 @@ class Repo:
c_issue_name = self.c_issue_name_fmt.format(issue_no=issue_no) c_issue_name = self.c_issue_name_fmt.format(issue_no=issue_no)
c_issues_sub_path = os.path.join(self.c_repo_path, "issues") c_issues_sub_path = os.path.join(self.c_repo_path, "issues")
results_key = None results_key = None
url = None
if issue_no is not None: if issue_no is not None:
if not os.path.isdir(c_issues_sub_path): if not os.path.isdir(c_issues_sub_path):
os.makedirs(c_issues_sub_path) os.makedirs(c_issues_sub_path)
c_issue_path = os.path.join(c_issues_sub_path, c_issue_name) c_issue_path = os.path.join(c_issues_sub_path, c_issue_name)
c_path = c_issue_path c_path = c_issue_path
make_list = True make_list = True
# Change query_s to the issue url (formerly issue_url): # Change url to the issue url (formerly issue_url):
query_s = self.api_issue_url_fmt.format( url = self.api_issue_url_fmt.format(
instance_url=self.instance_url, instance_url=self.instance_url,
ru=self.remote_user, ru=self.remote_user,
rn=self.repo_name, rn=self.repo_name,
issue_no=issue_no, issue_no=issue_no,
) )
else: else:
prev_query_s = query_s prev_query_s = url
if len(searchTermStr) > 0: if len(searchTermStr) > 0:
query_s = self.search_issues_url url = self.search_issues_url
results_key = self.search_results_key results_key = self.search_results_key
if "?" not in query_s: if "?" not in url:
query_s += "?q=" + searchTermStr url += "?q=" + searchTermStr
else: else:
query_s += searchTermStr url += searchTermStr
debug(" changing query_s from {} to {}" debug(" changing url from {} to {}"
"".format(prev_query_s, query_s)) "".format(prev_query_s, url))
debug(" (issues will be set to the content of the {}" debug(" (issues will be set to the content of the {}"
" element)".format(results_key)) " element)".format(results_key))
# Labels are NOT in the URL, but filtered later (See docstring). # Labels are NOT in the URL, but filtered later (See docstring).
if self.page is not None: if self.page is not None:
query_s = self.issues_url + "?page=" + str(self.page) url = self.issues_url + "?page=" + str(self.page)
query_s += and_query_part url += and_query_part
debug(" appended page query_part={} (c_path={})" debug(" appended page query_part={} (c_path={})"
"".format(query_part, c_path)) "".format(query_part, c_path))
elif len(query_part) > 0: elif len(query_part) > 0:
query_s += "?" + query_part url += "?" + query_part
debug(" appended custom query_part={} (c_path={})" debug(" appended custom query_part={} (c_path={})"
"".format(query_part, c_path)) "".format(query_part, c_path))
else: else:
debug(" There was no custom query.") debug(" There was no custom query.")
self.last_query_s = query_s self.last_url = url
# ^ Differs from self.last_src, which can be a file. # ^ Differs from self.last_src, which can be a file.
@ -876,7 +880,7 @@ class Repo:
if not quiet: if not quiet:
print(p+"Loading cache: \"{}\"".format(c_path)) print(p+"Loading cache: \"{}\"".format(c_path))
debug(p+"Cache time limit: {}".format(max_cache_delta)) debug(p+"Cache time limit: {}".format(max_cache_delta))
debug(p+" for URL: {}".format(query_s)) debug(p+" for URL: {}".format(url))
if not quiet: if not quiet:
print(p+"Cache expires: {}".format(expires_s)) print(p+"Cache expires: {}".format(expires_s))
with open(c_path) as json_file: with open(c_path) as json_file:
@ -935,25 +939,25 @@ class Repo:
c_path c_path
)) ))
self.last_src = query_s self.last_src = url
# ^ If didn't return yet, the source is a URL. # ^ If didn't return yet, the source is a URL.
req_is_complex = False req_is_complex = False
try: try:
debug(p+"Query URL (query_s): {}".format(query_s)) debug(p+"Query URL (url): {}".format(url))
headers = {} headers = {}
token = self.options.get('token') token = self.options.get('token')
if token is not None: if token is not None:
headers['Authorization'] = "token " + token headers['Authorization'] = "token " + token
if len(headers) > 0: if len(headers) > 0:
req_is_complex = True req_is_complex = True
response = requests.get(query_s, headers=headers) res = requests.get(url, headers=headers)
# response = req.urlopen(query_s) # res = req.urlopen(url)
res_text = response.text res_text = res.text
# NOTE: In python3, response.content is in bytes # NOTE: In python3, res.content is in bytes
# (<https://stackoverflow.com/a/18810889/4541104>). # (<https://stackoverflow.com/a/18810889/4541104>).
else: else:
response = request.urlopen(query_s) res = request.urlopen(url)
res_text = decode_safe(response.read()) res_text = decode_safe(res.read())
except HTTPError as ex: except HTTPError as ex:
msg = ex.reason msg = ex.reason
if ex.code == 410: if ex.code == 410:
@ -965,17 +969,17 @@ class Repo:
'code': ex.code, 'code': ex.code,
'reason': msg, 'reason': msg,
'headers': ex.headers, 'headers': ex.headers,
'url': query_s, 'url': url,
} }
) )
# msg = str(ex) + ": " + self.rateLimitFmt.format(query_s) # msg = str(ex) + ": " + self.rateLimitFmt.format(url)
return ( return (
None, None,
{ {
'code': ex.code, 'code': ex.code,
'reason': msg, 'reason': msg,
'headers': ex.headers, 'headers': ex.headers,
'url': query_s, 'url': url,
} }
) )
@ -1132,6 +1136,9 @@ class Repo:
fn += ".json" fn += ".json"
c_path += ".json" c_path += ".json"
else: else:
error("url: {}".format(url))
error("self.labels_url: {}".format(self.labels_url))
error("self.issues_url: {}".format(self.issues_url))
raise NotImplementedError("getCachedJsonDict" raise NotImplementedError("getCachedJsonDict"
" doesn't have a cache directory" " doesn't have a cache directory"
" for {}. Try --refresh" " for {}. Try --refresh"
@ -1187,10 +1194,10 @@ class Repo:
if token is not None: if token is not None:
headers['Authorization'] = "token " + token headers['Authorization'] = "token " + token
if len(headers) > 0: if len(headers) > 0:
res = requests.get(query_s, headers=headers) res = requests.get(url, headers=headers)
# res = req.urlopen(query_s) # res = req.urlopen(url)
res_text = response.text res_text = res.text
# NOTE: In python3, response.content is in bytes # NOTE: In python3, res.content is in bytes
# (<https://stackoverflow.com/a/18810889/4541104>). # (<https://stackoverflow.com/a/18810889/4541104>).
else: else:
res = request.urlopen(url) res = request.urlopen(url)
@ -1223,7 +1230,8 @@ class Repo:
return data, None return data, None
def show_issue(self, issue, refresh=False, never_expire=False): def show_issue(self, issue, refresh=False, never_expire=False,
quiet=False):
''' '''
Display an issue dictionary as formatted text after getting the Display an issue dictionary as formatted text after getting the
issue body and other data from the internet. Gather all of the issue body and other data from the internet. Gather all of the
@ -1245,11 +1253,15 @@ class Repo:
else 'code' is not present). else 'code' is not present).
''' '''
p = self.log_prefix p = self.log_prefix
print("") shmsg = print
if quiet:
shmsg = debug
shmsg("")
debug("show_issue...") debug("show_issue...")
print("#{} {}".format(issue["number"], issue["title"])) shmsg("#{} {}".format(issue["number"], issue["title"]))
# print(issue["html_url"]) # shmsg(issue["html_url"])
print("") shmsg("")
issue_data = issue issue_data = issue
html_url = issue['html_url'] html_url = issue['html_url']
issue_data, err = self.getCachedJsonDict( issue_data, err = self.getCachedJsonDict(
@ -1272,42 +1284,54 @@ class Repo:
# ^ left_w must be >=11 or "updated_at:" will push the next col. # ^ left_w must be >=11 or "updated_at:" will push the next col.
spacer = " " spacer = " "
line_fmt = "{: <" + str(left_w) + "}" + spacer + "{}" line_fmt = "{: <" + str(left_w) + "}" + spacer + "{}"
print(line_fmt.format("html_url:", issue["html_url"])) shmsg(line_fmt.format("html_url:", issue["html_url"]))
print(line_fmt.format("by:", issue_data["user"]["login"])) shmsg(line_fmt.format("by:", issue_data["user"]["login"]))
print(line_fmt.format("state:", issue_data["state"])) shmsg(line_fmt.format("state:", issue_data["state"]))
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:", shmsg(line_fmt.format("assignees:",
" ".join(assignee_names))) " ".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)) shmsg(line_fmt.format("assignee:", assignee_name))
labels_s = "None" labels_s = "None"
if len(issue['lower_labels']) > 0: ll = None
if issue.get('labels') is not None:
if issue.get('lower_labels') is None:
issue['lower_labels'] = []
for oldL in issue.get('labels'):
labelURL = oldL.get('url')
if labelURL is not None:
lastSlashI = labelURL.rfind('/')
if lastSlashI > -1:
ll = labelURL[lastSlashI+1:]
issue['lower_labels'].append(ll)
lls = issue.get('lower_labels')
if (lls is not None) and (len(issue['lower_labels']) > 0):
neat_labels = [] neat_labels = []
for label_s in issue['lower_labels']: for label_s in lls:
if " " in label_s: if " " in label_s:
neat_labels.append('"' + label_s + '"') neat_labels.append('"' + label_s + '"')
else: else:
neat_labels.append(label_s) neat_labels.append(label_s)
labels_s = ", ".join(neat_labels) labels_s = ", ".join(neat_labels)
# moved further down: print(line_fmt.format("labels:", labels_s)) # moved further down: shmsg(line_fmt.format("labels:", labels_s))
print("") shmsg("")
if markdown is not None: if markdown is not None:
print('"""') shmsg('"""')
print(markdown) shmsg(markdown)
print('"""') shmsg('"""')
else: else:
print("(no description)") shmsg("(no description)")
comments = issue_data.get("comments") comments = issue_data.get("comments")
if comments is None: if comments is None:
comments = 0 comments = 0
if comments > 0: if comments > 0:
print("") shmsg("")
print("") shmsg("")
print("({}) comment(s):".format(comments)) shmsg("({}) comment(s):".format(comments))
left_margin = " " left_margin = " "
c_prop_fmt = (left_margin + "{: <" + str(left_w) + "}" c_prop_fmt = (left_margin + "{: <" + str(left_w) + "}"
@ -1386,22 +1410,22 @@ class Repo:
login = None login = None
if user is not None: if user is not None:
print("") shmsg("")
print("") shmsg("")
login = user.get('login') login = user.get('login')
if login is not None: if login is not None:
print(c_prop_fmt.format("from:", login)) shmsg(c_prop_fmt.format("from:", login))
updated_at = evt.get("updated_at") updated_at = evt.get("updated_at")
if updated_at is not None: if updated_at is not None:
print(c_prop_fmt.format("updated_at:", shmsg(c_prop_fmt.format("updated_at:",
updated_at)) updated_at))
body = evt.get("body") body = evt.get("body")
if body is not None: if body is not None:
print("") shmsg("")
print(left_margin + '"""') shmsg(left_margin + '"""')
print(left_margin + body) shmsg(left_margin + body)
print(left_margin + '"""') shmsg(left_margin + '"""')
print("") shmsg("")
rename = evt.get('rename') rename = evt.get('rename')
event = evt.get('event') event = evt.get('event')
@ -1421,7 +1445,7 @@ class Repo:
source_issue = source.get('issue') source_issue = source.get('issue')
if source_issue is not None: if source_issue is not None:
source_number = source_issue.get('number') source_number = source_issue.get('number')
print(left_margin shmsg(left_margin
+"cross-reference: {} referenced this issue" +"cross-reference: {} referenced this issue"
" in {} {}." " in {} {}."
"".format(login, source_type, source_number)) "".format(login, source_type, source_number))
@ -1430,7 +1454,7 @@ class Repo:
if label is not None: if label is not None:
label_name = label.get('name') label_name = label.get('name')
label_color = label.get('color') label_color = label.get('color')
print(left_margin+"{}: {} by {} {}" shmsg(left_margin+"{}: {} by {} {}"
"".format(event, label_name, login, "".format(event, label_name, login,
created_at)) created_at))
# elif (event == "closed") or (event == "reopened"): # elif (event == "closed") or (event == "reopened"):
@ -1442,17 +1466,17 @@ class Repo:
if label is not None: if label is not None:
label_name = label.get('name') label_name = label.get('name')
label_color = label.get('color') label_color = label.get('color')
print(left_margin+"{}: {} by {} {}" shmsg(left_margin+"{}: {} by {} {}"
"".format(event, label_name, login, "".format(event, label_name, login,
created_at)) created_at))
else: else:
print(left_margin+"{} {} by {}" shmsg(left_margin+"{} {} by {}"
"".format(event.upper(), created_at, login)) "".format(event.upper(), created_at, login))
if (rename is not None) and ('renamed' not in ignore_events): if (rename is not None) and ('renamed' not in ignore_events):
# already said "RENAMED" above (evt.get('event')) # already said "RENAMED" above (evt.get('event'))
# print(left_margin+"renamed issue") # shmsg(left_margin+"renamed issue")
print(left_margin+" from:{}".format(rename.get('from'))) shmsg(left_margin+" from:{}".format(rename.get('from')))
print(left_margin+" to:{}".format(rename.get('to'))) shmsg(left_margin+" to:{}".format(rename.get('to')))
reactions = evt.get('reactions') reactions = evt.get('reactions')
reactions_url = None reactions_url = None
@ -1480,10 +1504,10 @@ class Repo:
if reac_user is not None: if reac_user is not None:
reac_login = reac_user.get('login') reac_login = reac_user.get('login')
reac_content = reac.get('content') reac_content = reac.get('content')
print(left_margin + "- <{}> :{}:" shmsg(left_margin + "- <{}> :{}:"
"".format(reac_login, reac_content)) "".format(reac_login, reac_content))
print("") shmsg("")
print(left_margin+"labels: {}".format(labels_s)) shmsg(left_margin+"labels: {}".format(labels_s))
closed_by = issue_data.get('closed_by') closed_by = issue_data.get('closed_by')
closed_at = issue_data.get('closed_at') closed_at = issue_data.get('closed_at')
if (closed_by is not None) or (closed_at is not None): if (closed_by is not None) or (closed_at is not None):
@ -1491,7 +1515,7 @@ class Repo:
# (determine this by getting 'state'). # (determine this by getting 'state').
# The "REOPENED" and "CLOSED" events also appear in the # The "REOPENED" and "CLOSED" events also appear in the
# timeline (see this_tmln_json_url). # timeline (see this_tmln_json_url).
print() shmsg()
state = issue_data.get('state') state = issue_data.get('state')
if state is None: if state is None:
state = options['default_query'].get('state') state = options['default_query'].get('state')
@ -1499,21 +1523,23 @@ class Repo:
if closed_at is not None: if closed_at is not None:
closet_at_str = " {}".format(closed_at) closet_at_str = " {}".format(closed_at)
if state != "open": if state != "open":
closed_by_login = closed_by.get("login") closed_by_login = None
if closed_by is not None:
closed_by_login = closed_by.get("login")
if closed_by_login is not None: if closed_by_login is not None:
print(" (CLOSED{} by {})".format( shmsg(" (CLOSED{} by {})".format(
closet_at_str, closet_at_str,
closed_by_login closed_by_login
)) ))
else: else:
print(" (CLOSED{})".format(closet_at_str)) shmsg(" (CLOSED{})".format(closet_at_str))
if state == "open": if state == "open":
print(" (REOPENED)") shmsg(" (REOPENED)")
elif closed_at is None: elif closed_at is None:
print(" (The closing date is unknown.)") shmsg(" (The closing date is unknown.)")
print("") shmsg("")
print("") shmsg("")
err = None err = None
if msg is not None: if msg is not None:
err = { err = {
@ -1915,7 +1941,7 @@ def main():
# ^ Never refresh, since that would already have been done. # ^ Never refresh, since that would already have been done.
state_msg = repo.default_query.get('state') state_msg = repo.default_query.get('state')
if state_msg is None: if state_msg is None:
state_msg = repo.last_query_s state_msg = repo.last_url
if state_msg != "open": if state_msg != "open":
print("(showing {} issue(s))".format(state_msg.upper())) print("(showing {} issue(s))".format(state_msg.upper()))
# ^ such as CLOSED # ^ such as CLOSED

11
utilities/pyissuesyncd

@ -110,6 +110,15 @@ def get_issue(repo, options, issue_no):
options, options,
issue_no=issue_no, issue_no=issue_no,
) )
if err is None:
never_expire = options.get('never_expire') is True
result, err = repo.show_issue(
results[0],
never_expire=never_expire,
quiet=True,
)
results = [result]
# ^ Get all of the details.
if results is None: if results is None:
if err is not None: if err is not None:
if err.get('code') == 410: if err.get('code') == 410:
@ -299,7 +308,7 @@ def start_issuesyncd(src_options, dst_options):
"".format(issue_no, src_updated_ts, src_updated_dt)) "".format(issue_no, src_updated_ts, src_updated_dt))
''' '''
# print(json.dumps(src_issue, indent=2)) # print(json.dumps(src_issue, indent=2))
continue # for debug only (if stuff below isn't implemented)
# enissue.set_verbose(True) # enissue.set_verbose(True)
dst_repo = Repo(dst_options) dst_repo = Repo(dst_options)
dst_issue, err = get_issue(dst_repo, dst_options, issue_no) dst_issue, err = get_issue(dst_repo, dst_options, issue_no)

Loading…
Cancel
Save